AtCoder Beginner Contest 270 Solution更好的阅读体验戳此进入题面链接题面 Luogu 链接abc不说了(该说不说这一场前三题分类讨论是真麻[ABC270D] Stones题面SolutionCode[ABC270E] Apple Baskets on Circle题面SolutionCode[ABC270F] Transportation题面SolutionCode[ABC270G] Sequence in mod P题面SolutionCode[ABC270Ex] add 1题面SolutionCodeUPD
Takahashi 和 Aoki 在玩一个取石子的游戏。
刚开始,有
现在,他们要按照以下规则轮流取石子:
现在,Takahashi 先取石子,Aoki 后取石子。 他们都想尽可能的最大化他们自己取走的石子数量。
若他们都以最优策略取石子,最后 Takahashi 会取走多少块石子?
开始以为是无脑贪心(主要是 D 题我以为会是很水那种
然后发现贪心是假的,就类似背包不能无脑装最大一样。所以考虑 DP,最开始想到
x1
2
3
4
5
6
7
8
9
10
11using namespace std;
12
13mt19937 rnd(random_device{}());
14int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
15bool rnddd(int x){return rndd(1, 100) <= x;}
16
17typedef unsigned int uint;
18typedef unsigned long long unll;
19typedef long long ll;
20typedef long double ld;
21
22
23
24template < typename T = int >
25inline T read(void);
26
27int N, K;
28int A[110];
29int dp[11000][2];
30// int ans(0); bool flag(true);
31// basic_string < int > rem;
32
33int main(){
34 N = read(), K = read();
35 for(int i = 1; i <= K; ++i)A[i] = read();
36 for(int i = 1; i <= N; ++i){
37 for(int j = K; j >= 1; --j){
38 if(i >= A[j])
39 dp[i][0] = max(dp[i][0], dp[i - A[j]][1] + A[j]),
40 dp[i][1] = i - dp[i][0];
41 // else
42 // dp[i][j][0] = dp[i][1], dp[i][1] = dp[i][0];
43 }
44 }//int ans(0);
45 // for(int i = 1; i <= K; ++i)ans = max(ans, dp[N][i][0]);
46 printf("%d\n", dp[N][0]);
47 // for(int i = 1; i <= K; ++i)rem += read();
48 // while(!rem.empty() && N){
49 // while(!rem.empty() && rem.back() > N)rem.pop_back();
50 // if(rem.empty())break;
51 // ans += rem.back() * flag, flag ^= true, N -= rem.back();
52 // }printf("%d\n", ans);
53
54
55 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
56 return 0;
57}
58
59template < typename T >
60inline T read(void){
61 T ret(0);
62 int flag(1);
63 char c = getchar();
64 while(c != '-' && !isdigit(c))c = getchar();
65 if(c == '-')flag = -1, c = getchar();
66 while(isdigit(c)){
67 ret *= 10;
68 ret += int(c - '0');
69 c = getchar();
70 }
71 ret *= flag;
72 return ret;
73}
存在
题解区怎么都是二分答案的做法,来一发 VP 时糊出来的更加无脑的模拟做法。
首先假设一圈里有
然后注意其中有一步 __int128_t
。
xxxxxxxxxx
601
2
3
4
5
6
7
8
9
10
11using namespace std;
12
13mt19937 rnd(random_device{}());
14int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
15bool rnddd(int x){return rndd(1, 100) <= x;}
16
17typedef unsigned int uint;
18typedef unsigned long long unll;
19typedef long long ll;
20typedef long double ld;
21
22template < typename T = int >
23inline T read(void);
24
25int N; ll K;
26ll A[210000];
27priority_queue < ll, vector < ll >, greater < ll > > buc;
28
29int main(){
30 N = read(), K = read < ll >();
31 ll cur(0);
32 for(int i = 1; i <= N; ++i)if((A[i] = read < ll >()))buc.push(A[i]), ++cur;
33 ll minus(0);
34 while(!buc.empty()){
35 ll v = buc.top() - minus; buc.pop();
36 if((__int128_t)cur * v <= K)K -= cur * v, minus += v, --cur;
37 else{minus += K / cur, K -= K / cur * cur; break;}
38 }
39 for(int i = 1; i <= N; ++i)A[i] = max(0ll, A[i] - minus);
40 for(int i = 1; i <= N && K; ++i)if(A[i])--A[i], --K;
41 for(int i = 1; i <= N; ++i)printf("%lld%c", A[i], i == N ? '\n' : ' ');
42 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
43 return 0;
44}
45
46template < typename T >
47inline T read(void){
48 T ret(0);
49 int flag(1);
50 char c = getchar();
51 while(c != '-' && !isdigit(c))c = getchar();
52 if(c == '-')flag = -1, c = getchar();
53 while(isdigit(c)){
54 ret *= 10;
55 ret += int(c - '0');
56 c = getchar();
57 }
58 ret *= flag;
59 return ret;
60}
有
如果两个点
问至少花多少代价才能让所有点连通 .
考虑新建两个点,对
xxxxxxxxxx
1111
2
3
4
5
6
7
8
9
10
11using namespace std;
12
13mt19937 rnd(random_device{}());
14int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
15bool rnddd(int x){return rndd(1, 100) <= x;}
16
17typedef unsigned int uint;
18typedef unsigned long long unll;
19typedef long long ll;
20typedef long double ld;
21
22
23
24template < typename T = int >
25inline T read(void);
26
27// struct Edge{
28// Edge* nxt;
29// int to;
30// ll val;
31// OPNEW;
32// }ed[410000];
33// ROPNEW;
34// Edge* head[210000];
35
36ll X[210000], Y[210000];
37int N, M;
38ll ans(LONG_LONG_MAX);
39
40struct Edge{
41 int s, t; ll val;
42 friend const bool operator < (const Edge &a, const Edge &b){
43 return a.val < b.val;
44 }
45};
46basic_string < Edge > edgs, basic_edgs;
47
48class UnionFind{
49private:
50 int fa[210000];
51public:
52 void Clear(void){for(int i = 1; i <= 201000; ++i)fa[i] = i;}
53 int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
54 void Union(int s, int t){fa[Find(s)] = fa[Find(t)];}
55}uf;
56
57ll MST(void){
58 uf.Clear();
59 ll ret(0);
60 sort(edgs.begin(), edgs.end());
61 for(auto e : edgs)
62 if(uf.Find(e.s) != uf.Find(e.t))
63 uf.Union(e.s, e.t), ret += e.val;
64 for(int i = 1; i <= N - 1; ++i)if(uf.Find(i) != uf.Find(i + 1))return LONG_LONG_MAX;
65 return ret;
66}
67
68int main(){
69 N = read(), M = read();
70 for(int i = 1; i <= N; ++i)X[i] = read();
71 for(int i = 1; i <= N; ++i)Y[i] = read();
72 for(int i = 1; i <= M; ++i){
73 int s = read(), t = read(), v = read();
74 // head[s] = new Edge{head[s], t, v};
75 // head[t] = new Edge{head[t], s, v};
76 basic_edgs += Edge{s, t, v};
77 }edgs += basic_edgs;
78 ans = min(ans, MST());
79 // printf("%lld\n", ans);
80 for(int i = 1; i <= N; ++i)edgs += Edge{i, N + 1, X[i]};
81 ++N;
82 ans = min(ans, MST());
83 // printf("%lld\n", ans);
84 edgs.clear();
85 edgs += basic_edgs;
86 for(int i = 1; i <= N - 1; ++i)edgs += Edge{i, N, Y[i]};
87 ans = min(ans, MST());
88 // printf("%lld\n", ans);
89 for(int i = 1; i <= N - 1; ++i)edgs += Edge{i, N + 1, X[i]};
90 ++N;
91 ans = min(ans, MST());
92 printf("%lld\n", ans);
93 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
94 return 0;
95}
96
97template < typename T >
98inline T read(void){
99 T ret(0);
100 int flag(1);
101 char c = getchar();
102 while(c != '-' && !isdigit(c))c = getchar();
103 if(c == '-')flag = -1, c = getchar();
104 while(isdigit(c)){
105 ret *= 10;
106 ret += int(c - '0');
107 c = getchar();
108 }
109 ret *= flag;
110 return ret;
111}
对于某个无穷序列
求最小的 -1
。
多组数据,记
保证
保证
大概就是递推转通向,然后发现可以直接 BSGS 解决。
然后就是有一大堆特判的细节需要考虑一下。
xxxxxxxxxx
791
2
3
4
5
6
7
8
9
10
11using namespace std;
12
13mt19937 rnd(random_device{}());
14int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
15bool rnddd(int x){return rndd(1, 100) <= x;}
16
17typedef unsigned int uint;
18typedef unsigned long long unll;
19typedef long long ll;
20typedef long double ld;
21
22template < typename T = int >
23inline T read(void);
24
25ll MOD, A, B, S, G;
26unordered_map < ll, int > mp;
27
28ll qpow(ll a, ll b){
29 ll ret(1), mul(a);
30 while(b){
31 if(b & 1)ret = ret * mul % MOD;
32 b >>= 1;
33 mul = mul * mul % MOD;
34 }return ret;
35}
36ll inv(ll v){return qpow(v, MOD - 2);}
37ll BSGS(ll A, ll B){
38 mp.clear();
39 int lim = ceil(sqrt(MOD));
40 ll base(1), mul(1);
41 for(ll i = 1; i <= lim; ++i)(base *= A) %= MOD, mp[B * base % MOD] = i;
42 for(ll i = 1; i <= lim; ++i){
43 (mul *= base) %= MOD;
44 if(mp.find(mul) != mp.end())return i * lim - mp[mul];
45 }return -1;
46}
47
48int main(){
49 // freopen("in.txt", "r", stdin);
50 int T = read();
51 while(T--){
52 MOD = read(), A = read(), B = read(), S = read(), G = read();
53 ll V = (G + B * inv(A - 1) % MOD) % MOD * inv((S + B * inv(A - 1) % MOD) % MOD) % MOD;
54 if(S == G)printf("0\n");
55 else if(!A)printf("%d\n", B == G ? 1 : -1);
56 else if(A == 1){
57 if(!B)printf("-1\n");
58 else printf("%lld\n", ((G - S) * inv(B) % MOD + MOD) % MOD);
59 }else printf("%lld\n", BSGS(A, V));
60 }
61 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
62 return 0;
63}
64
65template < typename T >
66inline T read(void){
67 T ret(0);
68 int flag(1);
69 char c = getchar();
70 while(c != '-' && !isdigit(c))c = getchar();
71 if(c == '-')flag = -1, c = getchar();
72 while(isdigit(c)){
73 ret *= 10;
74 ret += int(c - '0');
75 c = getchar();
76 }
77 ret *= flag;
78 return ret;
79}
给定序列
大概是一道没有什么高端的算法,仅靠推式子的难度评黑的题。
首先我们不难想到,如果设当前计数器的值为
然后不太严谨地思考一下不难发现,我们每次是使除了选择的其它的计数器都加一,所以拖后腿的一定是
于是此时不难想到一个较为简单的状态:令
显然
转化一下:
现在不难发现即较小的都在左侧,较大的都在右侧,不过这个转移仍然不行,也就是我们是已知
考虑令
显然
类比一下之前的推出来:
显然:
不难发现对于固定的
对于
xxxxxxxxxx
891
2
3
4
5
6
7
8
9
10
11using namespace std;
12
13mt19937 rnd(random_device{}());
14int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
15bool rnddd(int x){return rndd(1, 100) <= x;}
16
17typedef unsigned int uint;
18typedef unsigned long long unll;
19typedef long long ll;
20typedef long double ld;
21
22
23
24template < typename T = int >
25inline T read(void);
26
27struct Matrix2{
28 ll v00, v01, v10, v11;
29 friend Matrix2 operator * (const Matrix2 &a, const Matrix2 &b){
30 return Matrix2{
31 (a.v00 * b.v00 % MOD + a.v01 * b.v10 % MOD) % MOD,
32 (a.v00 * b.v01 % MOD + a.v01 * b.v11 % MOD) % MOD,
33 (a.v10 * b.v00 % MOD + a.v11 * b.v10 % MOD) % MOD,
34 (a.v10 * b.v01 % MOD + a.v11 * b.v11 % MOD) % MOD
35 };
36 }
37};
38Matrix2 base{1, 0, 0, 1};
39
40ll qpow(ll a, ll b){
41 ll ret(1), mul(a);
42 while(b){
43 if(b & 1)ret = ret * mul % MOD;
44 b >>= 1;
45 mul = mul * mul % MOD;
46 }return ret;
47}
48ll inv(ll v){return qpow(v, MOD - 2);}
49Matrix2 qpow(Matrix2 a, ll b){
50 Matrix2 ret(base), mul(a);
51 while(b){
52 if(b & 1)ret = ret * mul;
53 b >>= 1;
54 mul = mul * mul;
55 }return ret;
56}
57
58int N;
59ll A[210000];
60
61int main(){
62 N = read();
63 for(int i = 1; i <= N; ++i)A[i] = read < ll >();
64 Matrix2 ans{0, 1, 0, 0};
65 ll cur(0);
66 for(int i = N - 1; i >= 1; --i){
67 ll B = N * inv(i) % MOD, C = (N - cur) * inv(i) % MOD;
68 ans = ans * qpow(Matrix2{B, 0, C, 1}, A[i + 1] - A[i]);
69 (cur += ans.v00) %= MOD;
70 }printf("%lld\n", (ans.v00 % MOD + MOD) % MOD);
71 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
72 return 0;
73}
74
75template < typename T >
76inline T read(void){
77 T ret(0);
78 int flag(1);
79 char c = getchar();
80 while(c != '-' && !isdigit(c))c = getchar();
81 if(c == '-')flag = -1, c = getchar();
82 while(isdigit(c)){
83 ret *= 10;
84 ret += int(c - '0');
85 c = getchar();
86 }
87 ret *= flag;
88 return ret;
89}
update-2023_01_27 初稿