AtCoder Beginner Contest 270 题解

AtCoder Beginner Contest 270 Solution

更好的阅读体验戳此进入

题面链接

题面 Luogu 链接

abc不说了(该说不说这一场前三题分类讨论是真麻

[ABC270D] Stones

题面

Takahashi 和 Aoki 在玩一个取石子的游戏。

刚开始,有 \(N\) 个石子,还有一个长度为 \(K\) 的序列 \(A = \{A_1,A_2,\cdots,A_K\}\)

现在,他们要按照以下规则轮流取石子:

  • 对于每次操作,他可以选择一个 \(i\)\(1 \leq i \leq K\)),这时他会取走 \(A_i\) 块石子。

  • 当一个人没法取石子时,游戏结束。

现在,Takahashi 先取石子,Aoki 后取石子。
他们都想尽可能的最大化他们自己取走的石子数量。

若他们都以最优策略取石子,最后 Takahashi 会取走多少块石子?

Solution

开始以为是无脑贪心(主要是 D 题我以为会是很水那种

然后发现贪心是假的,就类似背包不能无脑装最大一样。所以考虑 DP,最开始想到 $ dp(i, j, 0/1) $ 表示考虑前 $ i $ 个石子,考虑前 $ j $ 个序列 $ A $ 考虑先手还是后手,然后一直没调出来。后来发现直接去掉 $ j $ 这一维然后取个 $ \max $ 就可以了。当然似乎直接去掉 $ 0/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 void* Edge::operator new(size_t){static Edge* P = ed; 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, K;
int A[110];
int dp[11000][2];
// int ans(0); bool flag(true);
// basic_string < int > rem;

int main(){
    N = read(), K = read();
    for(int i = 1; i <= K; ++i)A[i] = read();
    for(int i = 1; i <= N; ++i){
        for(int j = K; j >= 1; --j){
            if(i >= A[j])
                dp[i][0] = max(dp[i][0], dp[i - A[j]][1] + A[j]),
                dp[i][1] = i - dp[i][0];
            // else
            //     dp[i][j][0] = dp[i][1], dp[i][1] = dp[i][0];
        }
    }//int ans(0);
    // for(int i = 1; i <= K; ++i)ans = max(ans, dp[N][i][0]);
    printf("%d\n", dp[N][0]);
    // for(int i = 1; i <= K; ++i)rem += read();
    // while(!rem.empty() && N){
    //     while(!rem.empty() && rem.back() > N)rem.pop_back();
    //     if(rem.empty())break;
    //     ans += rem.back() * flag, flag ^= true, N -= rem.back();
    // }printf("%d\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;
}

[ABC270E] Apple Baskets on Circle

题面

存在 $ n $ 个筐围成一圈,第 $ i $ 个有 $ A_i $ 个苹果。你需要从第 $ 1 $ 个筐开始依次拿掉一个苹果,直到拿了 $ k $ 个。如果筐内没有苹果那么直接拿下一个筐。求最终每个筐里还剩多少个苹果。

Solution

题解区怎么都是二分答案的做法,来一发 VP 时糊出来的更加无脑的模拟做法。

首先假设一圈里有 $ k $ 个篮子里有苹果,那么转一圈一定会拿走 $ k $ 个苹果,然后当拿空了一个篮子之后就会 $ k \leftarrow k - 1 $。所以我们考虑维护一下当前一共还有多少个篮子里有苹果,然后给所有 $ A_i $ 排个序,每次取最小的然后判断是否能拿空,如果可以的话那么直接拿空,否则尽量拿多圈,此时剩下的所有显然不足一圈,此时再次遍历一遍即可。

然后注意其中有一步 $ cur \times v $,这个东西我感觉似乎是 $ 10^{24} $ 级别的,于是开了个 __int128_t

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 void* Edge::operator new(size_t){static Edge* P = ed; 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; ll K;
ll A[210000];
priority_queue < ll, vector < ll >, greater < ll > > buc;

int main(){
    N = read(), K = read < ll >();
    ll cur(0);
    for(int i = 1; i <= N; ++i)if((A[i] = read < ll >()))buc.push(A[i]), ++cur;
    ll minus(0);
    while(!buc.empty()){
        ll v = buc.top() - minus; buc.pop();
        if((__int128_t)cur * v <= K)K -= cur * v, minus += v, --cur;
        else{minus += K / cur, K -= K / cur * cur; break;}
    }
    for(int i = 1; i <= N; ++i)A[i] = max(0ll, A[i] - minus);
    for(int i = 1; i <= N && K; ++i)if(A[i])--A[i], --K;
    for(int i = 1; i <= N; ++i)printf("%lld%c", A[i], i == N ? '\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;
}

[ABC270F] Transportation

题面

\(n\) 个点,如下操作:

  • 对于 \(1\le i\le n\),可以花 \(x_i\) 的贡 \(i\) 号点建一个机场 .
  • 对于 \(1\le i\le n\),可以花 \(y_i\) 的贡献在 \(i\) 号点建一个港口 .
  • 对于 \(1\le i\le n\),可以花 \(z_i\) 的贡献在 \(a_i\) 号点到 \(b_i\) 号点连一条无向边 .

如果两个点 \(u,v\) 满足下列条件之一,则 \(u,v\) 可以互相到达:

  • \(u,v\) 都有机场 .
  • \(u,v\) 都有港口 .
  • \(u\)\(v\) 有边 .

问至少花多少代价才能让所有点连通 .

\(1\le n,m\le 2\times 10^5\)\(1\le x_i,y_i,z_i\le 10^9\) .

Solution

考虑新建两个点,对 $ n $ 个点连边,边权分别为 $ X_i, Y_i $,然后 $ 2^2 $ 次枚举并跑 MST 取最优即可。

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 void* Edge::operator new(size_t){static Edge* P = ed; 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;
//     ll val;
//     OPNEW;
// }ed[410000];
// ROPNEW;
// Edge* head[210000];

ll X[210000], Y[210000];
int N, M;
ll ans(LONG_LONG_MAX);

struct Edge{
    int s, t; ll val;
    friend const bool operator < (const Edge &a, const Edge &b){
        return a.val < b.val;
    }
};
basic_string < Edge > edgs, basic_edgs;

class UnionFind{
private:
    int fa[210000];
public:
    void Clear(void){for(int i = 1; i <= 201000; ++i)fa[i] = i;}
    int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
    void Union(int s, int t){fa[Find(s)] = fa[Find(t)];}
}uf;

ll MST(void){
    uf.Clear();
    ll ret(0);
    sort(edgs.begin(), edgs.end());
    for(auto e : edgs)
        if(uf.Find(e.s) != uf.Find(e.t))
            uf.Union(e.s, e.t), ret += e.val;
    for(int i = 1; i <= N - 1; ++i)if(uf.Find(i) != uf.Find(i + 1))return LONG_LONG_MAX;
    return ret;
}

int main(){
    N = read(), M = read();
    for(int i = 1; i <= N; ++i)X[i] = read();
    for(int i = 1; i <= N; ++i)Y[i] = read();
    for(int i = 1; i <= M; ++i){
        int s = read(), t = read(), v = read();
        // head[s] = new Edge{head[s], t, v};
        // head[t] = new Edge{head[t], s, v};
        basic_edgs += Edge{s, t, v};
    }edgs += basic_edgs;
    ans = min(ans, MST());
    // printf("%lld\n", ans);
    for(int i = 1; i <= N; ++i)edgs += Edge{i, N + 1, X[i]};
    ++N;
    ans = min(ans, MST());
    // printf("%lld\n", ans);
    edgs.clear();
    edgs += basic_edgs;
    for(int i = 1; i <= N - 1; ++i)edgs += Edge{i, N, Y[i]};
    ans = min(ans, MST());
    // printf("%lld\n", ans);
    for(int i = 1; i <= N - 1; ++i)edgs += Edge{i, N + 1, X[i]};
    ++N;
    ans = min(ans, MST());
    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;
}

[ABC270G] Sequence in mod P

题面

对于某个无穷序列 \(\{X\}\),构造如下:

\[X_i = \begin{cases} S & i=0 \\ (A\times X_{i-1}+B)\bmod P & i \geq 1 \end{cases} \]

求最小的 \(i\) 满足 \(X_i=G\),没有输出 -1

多组数据,记 \(T\) 为数据组数,则有 \(1\le T\le100\)

保证 \(P\) 是质数,\(2\le P\le10^9\)

保证 \(0\le A,B,S,G< P\)

Solution

大概就是递推转通向,然后发现可以直接 BSGS 解决。

然后就是有一大堆特判的细节需要考虑一下。

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 void* Edge::operator new(size_t){static Edge* P = ed; 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);

ll MOD, A, B, S, G;
unordered_map < ll, int > mp;

ll qpow(ll a, ll b){
    ll ret(1), mul(a);
    while(b){
        if(b & 1)ret = ret * mul % MOD;
        b >>= 1;
        mul = mul * mul % MOD;
    }return ret;
}
ll inv(ll v){return qpow(v, MOD - 2);}
ll BSGS(ll A, ll B){
    mp.clear();
    int lim = ceil(sqrt(MOD));
    ll base(1), mul(1);
    for(ll i = 1; i <= lim; ++i)(base *= A) %= MOD, mp[B * base % MOD] = i;
    for(ll i = 1; i <= lim; ++i){
        (mul *= base) %= MOD;
        if(mp.find(mul) != mp.end())return i * lim - mp[mul];
    }return -1;
}

int main(){
    // freopen("in.txt", "r", stdin);
    int T = read();
    while(T--){
        MOD = read(), A = read(), B = read(), S = read(), G = read();
        ll V = (G + B * inv(A - 1) % MOD) % MOD * inv((S + B * inv(A - 1) % MOD) % MOD) % MOD;
        if(S == G)printf("0\n");
        else if(!A)printf("%d\n", B == G ? 1 : -1);
        else if(A == 1){
            if(!B)printf("-1\n");
            else printf("%lld\n", ((G - S) * inv(B) % MOD + MOD) % MOD);
        }else printf("%lld\n", BSGS(A, V));
    }
    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;
}

[ABC270Ex] add 1

题面

给定序列 $ A_n $,存在 $ n $ 个初始为 $ 0 $ 的计数器,每次可以进行如下操作:选定一个计数器使其变为 $ 0 $ 并使得其它所有计数器 $ +1 $。求期望的使对于每个 $ i $ 满足第 $ i $ 个计数器的数值不小于 $ A_i $ 的操作次数。对 $ 998244353 $ 取模。

Solution

大概是一道没有什么高端的算法,仅靠推式子的难度评黑的题。

首先我们不难想到,如果设当前计数器的值为 $ C_i $,那么我们的目的就是要满足所有 $ A_i - C_i \le 0 $。

然后不太严谨地思考一下不难发现,我们每次是使除了选择的其它的计数器都加一,所以拖后腿的一定是 $ \max{A_i - C_i} $,令 $ k = \max{A_i - C_i} $ 则状态将仅与 $ k $ 相关。

于是此时不难想到一个较为简单的状态:令 $ F(k) $ 表示状态为 $ k $ 时的期望操作次数。

显然 $ F(0) = 0 $,考虑转移,不难发现状态 $ k $ 肯定对应这一个或者一段 $ A $,因为序列 $ A $ 是不降的,所以我们假设第一个对应的前一个为 $ A_{idx} $,显然对于前 $ idx $ 个的操作都会使 $ k \leftarrow k - 1 $,对于 $ idx $ 之后的操作都会使当前的 $ k $ 变为新的 $ A_i $,所以转移显然为:

\[F(k) = \dfrac{idx}{N}F(k - 1) + \dfrac{1}{N} \sum_{i = idx + 1}^N F(A_i) + 1 \]

转化一下:

\[N \times F(k) = idx \times F(k - 1) + \sum_{i = idx + 1}^N F(A_i) + N \]

\[idx \times F(k - 1) = N \times F(k) - \sum_{i = idx + 1}^N F(A_i) - N \]

\[F(k - 1) = \dfrac{N \times (F(k) - 1) - \sum_{i = idx + 1}^N F(A_i)}{idx} \]

现在不难发现即较小的都在左侧,较大的都在右侧,不过这个转移仍然不行,也就是我们是已知 $ F(0) $ 然后想要求 $ F(A_n) $,但是这个式子却是需要反过来转移的,所以需要优化。

考虑令 $ G(k) $ 表示从状态 $ 0 $ 转移到状态 $ k $ 的期望次数,所以显然有 $ G(k) + F(k) = F(A_n) $。移个项然后带进去 $ F $,显然有:

\[F(A_n) - G(k) = \dfrac{idx}{N}((F(A_n) - G(k - 1)) + \dfrac{1}{N} \sum_{i = idx + 1}^N (F(A_n) - G(A_i)) + 1 \]

显然 $ F(A_n) $ 抵消了,则:

\[G(k) = \dfrac{idx}{N} G(k - 1) + \dfrac{1}{N} \sum_{i = idx + 1}^N G(A_i) - 1 \]

类比一下之前的推出来:

\[G(k - 1) = \dfrac{N(G(k) + 1) - \sum_{i = idx + 1}^N G(A_i)}{idx} \]

显然:$ G(A_n) = 0 $,我们要求 $ G(0) $,符合转移。

不难发现对于固定的 $ k $ 一定对应着固定的 $ idx $,也就是 $ \dfrac{N - \sum_{i = idx + 1}^N G(A_i)}{idx} $ 可以认为是一个常数,令其为 $ C $,若再令 $ B = \dfrac{N}{idx} $,所以有 $ G(k - 1) = B \times G(k) + C $,属于较为简单的转移,考虑矩乘优化,构造矩阵的过程也是平凡的,得到:

\[\begin{pmatrix} G(k) & 1 \end{pmatrix} \times \begin{pmatrix} B & 0 \\ C & 1 \end{pmatrix} = \begin{pmatrix} G(k - 1), 1 \end{pmatrix} \]

对于 $ [A_i, A_{i + 1}] $ 之间的部分的所有 $ k $ 显然 $ B $ 和 $ C $ 是相同的,这一部分用矩阵快速幂优化一下即可,最后复杂度应为 $ O(2^3 n \log A_i) $。

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 void* Edge::operator new(size_t){static Edge* P = ed; 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 (998244353ll)

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

struct Matrix2{
    ll v00, v01, v10, v11;
    friend Matrix2 operator * (const Matrix2 &a, const Matrix2 &b){
        return Matrix2{
            (a.v00 * b.v00 % MOD + a.v01 * b.v10 % MOD) % MOD,
            (a.v00 * b.v01 % MOD + a.v01 * b.v11 % MOD) % MOD,
            (a.v10 * b.v00 % MOD + a.v11 * b.v10 % MOD) % MOD,
            (a.v10 * b.v01 % MOD + a.v11 * b.v11 % MOD) % MOD
        };
    }
};
Matrix2 base{1, 0, 0, 1};

ll qpow(ll a, ll b){
    ll ret(1), mul(a);
    while(b){
        if(b & 1)ret = ret * mul % MOD;
        b >>= 1;
        mul = mul * mul % MOD;
    }return ret;
}
ll inv(ll v){return qpow(v, MOD - 2);}
Matrix2 qpow(Matrix2 a, ll b){
    Matrix2 ret(base), mul(a);
    while(b){
        if(b & 1)ret = ret * mul;
        b >>= 1;
        mul = mul * mul;
    }return ret;
}

int N;
ll A[210000];

int main(){
    N = read();
    for(int i = 1; i <= N; ++i)A[i] = read < ll >();
    Matrix2 ans{0, 1, 0, 0};
    ll cur(0);
    for(int i = N - 1; i >= 1; --i){
        ll B = N * inv(i) % MOD, C = (N - cur) * inv(i) % MOD;
        ans = ans * qpow(Matrix2{B, 0, C, 1}, A[i + 1] - A[i]);
        (cur += ans.v00) %= MOD;
    }printf("%lld\n", (ans.v00 % MOD + MOD) % MOD);
    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-2023_01_27 初稿

posted @ 2023-02-15 19:08  Tsawke  阅读(92)  评论(0编辑  收藏  举报