首页 > 其他分享 >AtCoder Beginner Contest 251 题解

AtCoder Beginner Contest 251 题解

时间:2023-01-07 15:47:45浏览次数:60  
标签:AtCoder int 题解 bmod ret choose 251 void define

AtCoder Beginner Contest 251 Solution

目录

更好的阅读体验戳此进入

题面链接

题面 Luogu 链接

老样子abc太水了就跳了

[ABC251D] At Most 3 (Contestant ver.)

题面

给你整数 \(W(1 \le W \le 10^6)\)。 你必须构造一个数组 \(a\),包含最多 \(300\) 个元素,使得小于等于 \(W\) 的所有正整数都可以被 \(a\) 数组的元素相加表示出来。

Solution

显然按照十进制拆分成三组即可,即 $ [1, 99], [100, 9900], [10000, 990000] $ 即可,共 $ 99 \times 3 = 297 $ 个。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template < typename T = int > 
inline T read(void);

int main(){
    printf("297\n");
    for(int i = 1; i <= 99; ++i)printf("%d %d %d ", i, i * 100, i * 10000);
    printf("\n");
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

[ABC251E] Takahashi and Animals

题面

有 \(n\) 只动物围成一圈,你可以花费 \(a[i]\) 喂食动物 \(i\) 和 \(i+1\)。特别地,你可以花费 \(a[n]\) 喂食动物 \(n\) 和 \(1\)。

输出喂食所有动物需要的最小花费。

Solution

DP 显然,令 $ dp(i, 0/1) $ 表示考虑前 $ i $ 个元素且最后一个元素不喂 / 喂的最小花费。转移显然,注意要考虑一下 $ N $ 和 $ 1 $ 之间,具体选择哪个即可。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template < typename T = int >
inline T read(void);

int N;
int a[310000];
ll dp[310000][2];

int main(){
    N = read();
    for(int i = 1; i <= N; ++i)a[i] = read();
    memset(dp, 0x3f, sizeof dp);
    dp[1][1] = a[1];
    for(int i = 2; i <= N; ++i)
        dp[i][0] = dp[i - 1][1],
        dp[i][1] = min(dp[i - 1][1], dp[i - 1][0]) + a[i];
    // for(int i = 1; i <= N; ++i)printf("%d: 0 = %lld, 1 = %lld\n", i, dp[i][0], dp[i][1]);
    ll ans = min(dp[N][1], dp[N][0]);
    dp[1][0] = 0;
    for(int i = 2; i <= N; ++i)
        dp[i][0] = dp[i - 1][1],
        dp[i][1] = min(dp[i - 1][1], dp[i - 1][0]) + a[i];
    ans = min(ans, dp[N][1]);
    printf("%lld\n", ans);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

[ABC251F] Two Spanning Trees

题面

给定无向连通简单图,求其两个以 $ 1 $ 为根的生成树 $ T_1, T_2 $,满足:

  • 对于 $ T_1 $,其任意一条非树边连结的两个节点均存在祖先关系,即一个点为另一个点的祖先。
  • 对于 $ T_2 $,其任意一条非树边连结的两个节点均不存在祖先关系。

Solution

前者对图 DFS,后者对图 BFS,证明显然。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template < typename T = int >
inline T read(void);

struct Edge{
    Edge* nxt;
    int to;
    OPNEW;
}ed[410000];
ROPNEW(ed);
Edge* head[410000];

int N, M;
bool vis[210000];

void dfs(int p = 1){
    vis[p] = true;
    for(auto i = head[p]; i; i = i->nxt){
        if(vis[SON])continue;
        printf("%d %d\n", p, SON);
        dfs(SON);
    }
}
void bfs(void){
    memset(vis, false, sizeof vis);
    queue < int > cur;
    cur.push(1), vis[1] = true;
    while(!cur.empty()){
        int tp = cur.front(); cur.pop();
        for(auto i = head[tp]; i; i = i->nxt){
            if(vis[SON])continue;
            printf("%d %d\n", tp, SON);
            vis[SON] = true, cur.push(SON);
        }
    }
}

int main(){
    N = read(), M = read();
    for(int i = 1; i <= M; ++i){
        int s = read(), t = read();
        head[s] = new Edge{head[s], t};
        head[t] = new Edge{head[t], s};
    }dfs(), bfs();
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

G 是计算几何,暂时跳了,以后有机会再回来做

[ABC251Ex] Fill Triangle

题面

存在序列 $ a_n $,将其压缩后给定。具体地,给定序列 $ P $ 以如下形式:$ ((a_1, c_1), (a_2, c_2), \cdots, (a_m, c_m)) $,表示序列 $ a $ 中有 $ c_1 $ 个 $ a_1 \(,\) c_2 $ 个 $ a_2 $,以此类推,且按序拼接。令序列 $ a_n $ 为三角金字塔 $ B $ 的第 $ n $ 层,即 $ B(n, i) = a_i $。特别地,该三角金字塔的递推式为 $ B(i, j) = (B(i + 1, j) + B(i + 1, j + 1)) \bmod{7} $。给定 $ k $,求该三角金字塔第 $ k $ 层的序列。

Solution

首先可以尝试随意给定一个序列 $ a_n $ 并打表,或手推一下,假设 $ n = 5 $,有:

$ a_1 + 4a_2 + 6a_3 + 4a_4 + a_5 $
$ a_1 + 3a_2 + 3a_3 + a_4 $ $ a_2 + 3a_3 + 3a_4 + a_5 $
$ a_1 + 2a_2 + a_3 $ $ a_2 + 2a_3 + a_4 $ $ a_3 + 2a_4 + a_5 $
$ a_1 + a_2 $ $ a_2 + a_3 $ $ a_3 + a_4 $ $ a_4 + a_5 $
$ a_1 $ $ a_2 $ $ a_3 $ $ a_4 $ $ a_5 $

不难发现这玩意每一项实际上就是二项式系数,或者说杨辉三角。并且还能发现,对于每一行的元素,它们的系数均相同,不同的只是将下标整体向右平移了一位。

此时我们便可以根据这个性质,求出对于第 $ k $ 层,第 $ x $ 列的元素,有:

\[B(k, x) = \sum_{i = 0}^{n - k}{n - k \choose i} a_{x + i} \bmod{7} \]

显然我们想要算出 $ k $ 层的所有元素,但是直接套这个式子暴力算是肯定过不了的。考虑优化。

显然序列 $ a $ 是由一段一段的相同元素构成的,而我们的查询就是在 $ a $ 里的查询一个区间然后按序将组合数乘上去。当我们从 $ B(k, x) $ 到 $ B(k, x + 1) $ 的时候,通过我们之前的结论可知需要将下标向右平移一位,而所有组合数的值不变,大概的过程如图:

Oops! The image is blocked! Please visit my own website to observe the image! 图片被墙了,请通过文章头的跳转链接访问!

观察发现,这个东西平移之后,对于大多数的 $ {n - k \choose i} \times a_{x + i} $ 变成 $ {n - k \choose i} \times a_{x + i + 1} $,都存在 $ a_{x + i} = a_{x + i + 1} $,而对于这些的贡献是完全不变的。不难想到这个变化操作的复杂度最多是 $ O(m) $ 的。所以当我们确定了 $ B(k, 1) $ 之后,后面的 $ B(k, 2) $ 到 $ B(k, k) $ 就都可以每次 $ O(m) $ 求解,故这步的复杂度优化为 $ O(mk) $。

具体地当我们移动询问区间的时候,遍历所有段,如果某一段的右端点在当前区间内,那么显然指向这一段的组合数会移动到下一段中,所以需要减掉前面的加上现在的。注意移动这步实际上是 $ O(mk \log n) $ 的,而且这只 $ \log $ 不小,所以会 TLE,则我们考虑预处理 Lucas。显然直接预处理 $ 10^9 $ 范围肯定不行,于是这里有个高妙的思路,我们本身的模数为 $ 7 $,那么我们可以先让值对 $ 7^k $ 取模,然后再对 $ 7 $ 取模,这个显然是等价的。而如果我们搞一个 $ 10^5 $ 左右的 $ 7^k $ 的话,那么一步 Lucas 之后需要求的组合数就都在 $ 10^5 $ 以内了,所以这个时候我们只需要预处理 $ 10^5 $ 以内的组合数,那么对于每次求 Lucas 直接拆一次但是不递归,直接查值乘起来取模即可,这样复杂度就是 $ O(1) $ 了,或者说 $ O(2) $,于是复杂度会变成 $ O(mk) $。同时不难发现,存在 $ $

于是现在的问题就仅剩如何求 $ B(k, 1) $,也就是求:

\[\sum_{i = 0}^{n - k}{n - k \choose i} a_{i + 1} \bmod{7} \]

首先还是考虑之前的思路,一定会有很多段的相同的 $ a $,假设我们存在一段 $ [l, r] $ 满足 $ \forall i \in [l, r), a_i = a_{i + 1} $,那么显然此处的值即为:

\[\sum_{i = l - 1}^{r - 1}{n - k \choose i}a_{i + 1} \bmod{7} \]

所以这个东西显然需要快速处理一段组合数,可以考虑做一个组合数的前缀和,这样即可 $ O(1) $ 查询一段组合数的求和,然后乘上 $ a_l $ 即可,这样每次的复杂度也就是 $ O(m) $ 了。当然我们肯定不能对于 $ n - k $ 以内的每个数求前缀和,所以可以对 $ m $ 段相同的分别求出对应的前缀和,然后查询即可。求的时候前面段的直接求和,最后剩下的不足一段的单独计算即可。

于是现在问题再次转化为如何快速求这种形式的组合数前缀和,可以参考该题:LG-P4345 [SHOI2015]超能粒子炮·改

具体地,对于求 $ F(n, k) = \sum_{i = 0}^k {n \choose i} \bmod p $,且 $ p $ 为质数,可以尝试通过 Lucas 进行处理(后面的式子最终都需要取模,这里省略):

\[\begin{aligned} \sum_{i = 0}^k {n \choose i} \bmod p &= \sum_{i = 0}^k {\lfloor \dfrac{n}{p} \rfloor \choose \lfloor \dfrac{i}{p} \rfloor} \times {n \bmod{p} \choose i \bmod{p}} \end{aligned} \]

然后发现对于前面的 $ \lfloor \dfrac{i}{p} \rfloor $,这个就是个数论分块,也可以按照这个思想,将原式化为:

\[{\lfloor \dfrac{n}{p} \rfloor \choose 0}\sum_{i = 0}^{p - 1} {n \bmod{p} \choose i \bmod{p}} + {\lfloor \dfrac{n}{p} \rfloor \choose 1}\sum_{i = 0}^{p - 1} {n \bmod{p} \choose i \bmod{p}} + \cdots + {\lfloor \dfrac{n}{p} \rfloor \choose \lfloor \dfrac{k}{p} \rfloor - 1}\sum_{i = 0}^{p - 1} {n \bmod{p} \choose i \bmod{p}} + {\lfloor \dfrac{n}{p} \rfloor \choose \lfloor \dfrac{k}{p} \rfloor}\sum_{i = 0}^{k \bmod{p}} {n \bmod{p} \choose i \bmod{p}} \]

然后继续转化:

\[\sum_{i = 0}^{p - 1} {n \bmod{p} \choose i \bmod{p}} \times \sum_{i = 0}^{\lfloor \dfrac{k}{p} \rfloor - 1}{\lfloor \dfrac{n}{p} \rfloor \choose i} + {\lfloor \dfrac{n}{p} \rfloor \choose \lfloor \dfrac{k}{p} \rfloor}\sum_{i =0}^{k \bmod{p}} {n \bmod{p} \choose i \bmod{p}} \]

此时对比我们之前设的 $ F(n, k) = \sum_{i = 0}^k {n \choose i} \bmod p $,所以原式可以再次化为:

\[F(n \bmod{p}, p - 1) \times F(\lfloor \dfrac{n}{p} \rfloor, \lfloor \dfrac{k}{p} \rfloor - 1) + {\lfloor \dfrac{n}{p} \rfloor \choose \lfloor \dfrac{k}{p} \rfloor} \times F(n \bmod{p}, k \bmod{p}) \]

然后我们只要随便与处理一下 $ p $ 以内的 $ F $,这样 $ F(n \bmod{p}, p - 1) $ 和 $ F(n \bmod{p}, k \bmod{p}) $ 即可 $ O(1) $ 查表,然后组合数直接用 Lucas 暴力算一下即可,对于 $ F(\lfloor \dfrac{n}{p} \rfloor, \lfloor \dfrac{k}{p} \rfloor - 1) $ 递归下去即可。

考虑这个东西的复杂度,用主定理推导一下即可:

\[T(n) = T(\lfloor \dfrac{n}{p} \rfloor) + f(n) \]

$ O(n) $ 处理一下组合数,则 $ f(n) $ 的复杂度只有 Lucas 的一只 $ \log $,所以最终这里单次求解的复杂度为 $ O(\log^2 n) $,然后我们要处理 $ m $ 个,则最终这步的复杂度为 $ O(m \log^2 n) $。最终复杂度为 $ O(mk + m \log^2 n + k \log n) $,显然能过。

至此我们就终于完成了这道题。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define MOD (7)
#define SPL (117649) //7^6

template < typename T = int >
inline T read(void);

int f[10][10];
int N, M, K;
int origin(0);
int sumf[210];
int lucas_div[SPL + 10], lucas_mod[SPL + 10];
struct Blk{int l, r; int val;} blk[210];

int qpow(int a, int b){
    int ret(1), mul(a);
    while(b){
        if(b & 1)ret = ret * mul % MOD;
        b >>= 1;
        mul = mul * mul % MOD;
    }return ret;
}
int fact[10], inv[10];
void Init(void){
    fact[0] = 1;
    for(int i = 1; i < MOD; ++i)fact[i] = fact[i - 1] * i % MOD;
    inv[MOD - 1] = qpow(fact[MOD - 1], MOD - 2);
    for(int i = MOD - 2; i >= 0; --i)inv[i] = inv[i + 1] * (i + 1) % MOD;
}
int GetC(int n, int m){
    if(n < m)return 0;
    return fact[n] * inv[m] % MOD * inv[n - m] % MOD;
}
int Lucas(ll n, ll m){
    if(n < MOD && m < MOD)return GetC(n, m);
    return Lucas(n / MOD, m / MOD) * GetC(n % MOD, m % MOD) % MOD;
}
int O1Lucas(ll m){
    return (lucas_div[m / SPL] * lucas_mod[m % SPL]) % MOD;
}
void InitF(void){
    for(int i = 0; i <= MOD - 1; ++i)f[i][0] = f[0][i] = 1;
    for(int i = 1; i <= MOD - 1; ++i)
        for(int j = 1; j <= MOD - 1; ++j)
            f[i][j] = (f[i][j - 1] + Lucas(i, j)) % MOD;
}
int F(ll n, ll k){
    if(n < 0 || k < 0)return 0;
    if(n < MOD && k < MOD)return f[n][k];
    return (f[n % MOD][MOD - 1] * F(n / MOD, k / MOD - 1) % MOD + Lucas(n / MOD, k / MOD) * f[n % MOD][k % MOD] % MOD) % MOD;
}

int main(){
    Init(), InitF();
    N = read(), M = read(), K = read();
    for(int i = 0; i <= SPL; ++i)lucas_div[i] = Lucas((N - K) / SPL, i), lucas_mod[i] = Lucas((N - K) % SPL, i);
    int curl(1);
    for(int i = 1; i <= M; ++i)blk[i].val = read(), blk[i].l = curl, blk[i - 1].r = blk[i].l - 1, curl += read();
    blk[M].r = N; int lim = N - K + 1;
    for(int i = 1; i <= M; ++i)
        sumf[i] = (F(N - K, blk[i].r - 1) - F(N - K, blk[i].l - 2) + MOD) % MOD;
    for(int i = 1; i <= M; ++i){
        int l = blk[i].l, r = blk[i].r;
        if(l > lim)break;
        if(r <= lim)(origin += sumf[i] * blk[i].val % MOD) %= MOD;
        else (origin += (F(N - K, lim - 1) - F(N - K, l - 2) + MOD) % MOD * blk[i].val % MOD) %= MOD;
    }printf("%d ", origin);
    int cl = 1, cr = lim;
    for(int i = 2; i <= K; ++i){
        for(int m = 1; m <= M; ++m){
            int r = blk[m].r;
            if(cl <= r && r <= cr)
                origin = (origin - O1Lucas(r - cl) * blk[m].val % MOD + O1Lucas(r - cl) * blk[m + 1].val % MOD + MOD) % MOD;
        }++cl, ++cr;
        printf("%d ", origin);
    }printf("\n");
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

UPD

update-2022_12_02 初稿

标签:AtCoder,int,题解,bmod,ret,choose,251,void,define
From: https://www.cnblogs.com/tsawke/p/17032761.html

相关文章

  • [ABC254Ex] Multiply or Divide by 2 题解
    [ABC254Ex]MultiplyorDivideby2Solution目录[ABC254Ex]MultiplyorDivideby2Solution更好的阅读体验戳此进入题面SolutionCodeUPD更好的阅读体验戳此进入题......
  • P8773 题解
    前言题目传送门!更好的阅读体验?一道有趣的题目。思路对于一个数\(a_i\),如果有\(a_i\oplust=x\),显然\(t=a_i\oplusx\)。设\(loc_i\)表示上一个\(t\)出......
  • [ABC255F] Pre-order and In-order 题解
    [ABC255F]Pre-orderandIn-orderSolution目录[ABC255F]Pre-orderandIn-orderSolution更好的阅读体验戳此进入题面SolutionCodeUPD更好的阅读体验戳此进入题面给......
  • AtCoder Beginner Contest 255 题解
    AtCoderBeginnerContest255Solution目录AtCoderBeginnerContest255Solution更好的阅读体验戳此进入题面链接题面Luogu链接abcd跳了[ABC255E]LuckyNumbers题......
  • [ABC255G] Constrained Nim 题解
    [ABC255G]ConstrainedNimSolution目录[ABC255G]ConstrainedNimSolution更好的阅读体验戳此进入题面SolutionCodeUPD更好的阅读体验戳此进入题面一般Nim游戏基......
  • [ABC256F] Cumulative Cumulative Cumulative Sum 题解
    [ABC256F]CumulativeCumulativeCumulativeSumSolution目录[ABC256F]CumulativeCumulativeCumulativeSumSolution更好的阅读体验戳此进入题面SolutionCodeUPD更......
  • [ABC256E] Takahashi's Anguish 题解
    [ABC256E]Takahashi'sAnguishSolution目录[ABC256E]Takahashi'sAnguishSolution更好的阅读体验戳此进入题面SolutionCodeUPD更好的阅读体验戳此进入题面存在$n......
  • [ABC252E] Road Reduction 题解
    [ABC252E]RoadReductionSolution目录[ABC252E]RoadReductionSolution更好的阅读体验戳此进入题面SolutionCodeUPD更好的阅读体验戳此进入题面给定$n$个点$......
  • [ABC252D] Distinct Trio 题解
    [ABC252D]DistinctTrioSolution目录[ABC252D]DistinctTrioSolution更好的阅读体验戳此进入题面SolutionCodeUPD更好的阅读体验戳此进入题面给定序列$a_n$,求满......
  • P8865 [NOIP2022] 种花 题解
    P8865[NOIP2022]种花Solution目录P8865[NOIP2022]种花Solution更好的阅读体验戳此进入题面SolutionCodeUPD更好的阅读体验戳此进入题面大概就是在有障碍的网格图......