A.Oops, It’s Yesterday Twice More 思路:考虑先把所有袋鼠集中在一起然后再移动。因为有步数限制( )。那么分类讨论移动到四个角上,看哪个符号条件的就输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int mod = 1e9 + 7 ;const int N = 2e5 + 10 ;int n,a,b;int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); cin>>n>>a>>b; int x = 1 ,y = 1 ; if ((a-x)+(b-y)<=(n-1 )) { for (int i = 1 ;i <= n-1 ;i++)cout<<"L" ; for (int i = 1 ;i <= n-1 ;i++)cout<<"U" ; for (int i = 1 ;i <= b-y;i++)cout<<"R" ; for (int i = 1 ;i <= a-x;i++)cout<<"D" ; return 0 ; } x = 1 ,y = n; if ((a-x)+(y-b)<=(n-1 )) { for (int i = 1 ;i <= n-1 ;i++)cout<<"R" ; for (int i = 1 ;i <= n-1 ;i++)cout<<"U" ; for (int i = 1 ;i <= y-b;i++)cout<<"L" ; for (int i = 1 ;i <= a-x;i++)cout<<"D" ; return 0 ; } x = n,y = 1 ; if ((x-a)+(b-y)<=(n-1 )) { for (int i = 1 ;i <= n-1 ;i++)cout<<"L" ; for (int i = 1 ;i <= n-1 ;i++)cout<<"D" ; for (int i = 1 ;i <= b-y;i++)cout<<"R" ; for (int i = 1 ;i <= x-a;i++)cout<<"U" ; return 0 ; } x = n,y = n; if ((x-a)+(y-b)<=(n-1 )) { for (int i = 1 ;i <= n-1 ;i++)cout<<"R" ; for (int i = 1 ;i <= n-1 ;i++)cout<<"D" ; for (int i = 1 ;i <= y-b;i++)cout<<"L" ; for (int i = 1 ;i <= x-a;i++)cout<<"U" ; return 0 ; } return 0 ; }
C. Klee in Solitary Confinement 思路:
做法一:(公式推导)我们考虑枚举众数是谁,然后去写。对于众数是 的情况,只有 对它有贡献。那么考虑对于一段区间 ,我们对于众数是 情况的贡献是什么呢?

如上图所示:
为了更快的处理区间问题,我们先预处理出对于某个数 的前缀出现次数 ,和 的前缀出现次数
那么对于 ,对于众数是 的贡献是
移向得:
因为 是定值,那么我们只需要后面这个东西最大就行了。我们固定左界去枚举右界,取max就是答案。
还有一个问题,如何处理出这个东西?
我们先用$vectoridx[N]存 下 每 种 值 的 下 标 , 然 后 呢 , 对 于 众 数 是 x+k的 情 况 , 只 有 x是 对 它 有 贡 献 的 。 我 们 把 idx[x]和 idx[x+k]取 出 来 , 根 据 下 标 , 把 它 们 排 成 一 排 , 假 设 这 两 组 数 总 共 是 有 c个 , 那 for(1~c)对 两 组 数 做 前 缀 操 作 。 做 完 以 后 还 是 for(1~c)去 求 pre2[n]+(pre1[R]-pre1[L-1])-(pre2[R]-pre2[L-1])$的最大值。
对于时间复杂度:因为每个数 至多被访问 次( 和 ),那么时间复杂度 .
注意:下标有负数,我们做个偏移。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int mod = 1e9 + 7 ;const int N = 4e6 + 10 ,M = 2e6 ;vector<int >idx[N]; int pos[N];int cnt[N];int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int n,k; cin>>n>>k; ll maxn = -1e9 ; ll res = -1e18 ; for (int i = 1 ;i <= n; i++) { ll x; cin>>x; x += M; cnt[x]++; if (cnt[x]>res)res = cnt[x]; maxn = max (maxn,x); idx[x].push_back (i); } for (int i = 0 ;i <= 4e6 ; i++) { int c = 0 ; int x = i,y = i+k; if (y<0 ||y>4e6 )continue ; int sz1 = idx[x].size (),sz2 = idx[y].size (); if (y<0 ||y>4e6 ||sz1==0 ||sz2==0 )continue ; int cnt1 = 0 ,cnt2 = 0 ; while (cnt1 < sz1|| cnt2 < sz2) { if (cnt1 == sz1) pos[idx[y][cnt2]] = ++c,cnt2++; else if (cnt2 == sz2) pos[idx[x][cnt1]] = ++c,cnt1++; else { if (idx[x][cnt1] < idx[y][cnt2]) pos[idx[x][cnt1]] = ++c,cnt1++; else pos[idx[y][cnt2]] = ++c,cnt2++; } } vector<ll>pre1 (c+1 ),pre2 (c+1 ); for (auto j:idx[x]) pre1[pos[j]]++; for (auto j:idx[y]) pre2[pos[j]]++; for (int i = 1 ; i <= c; i++) pre1[i] += pre1[i-1 ],pre2[i] += pre2[i-1 ]; ll t = 0 ; for (int i = 1 ;i <= c; i++) { t += (pre1[i]-pre1[i-1 ])-(pre2[i]-pre2[i-1 ]); if (t<0 )t = 0 ; res = max (res,t + pre2[c]); } } cout<<res<<"\n" ; return 0 ; }
做法二:
考虑一个区间 带来的影响:
对于一个区间原本的数是 ,现在变成了 ,原本是 的数现在变成了 。
如果选择 是众数,那么对于这个所选区间内 的贡献是 , 的贡献是 。那么对于给定 ,我们只需要找出最大贡献区间即可。但是如果枚举每个 然后再去扫一遍看贡献肯定超时了,那怎么办呢?当我们枚举到第 个位置的时候,我们统计 和 产生的贡献,也就是说,我们 一遍相当于枚举了全部的 。
那么如何选区间呢?从开始一直去取,直到某个位置贡献小于0了,那就不要这段了,因为从这段负的继续加不会更优。所以我们把这段区间丢掉,从新开始计算贡献。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int mod = 1e9 + 7 ;const int N = 4e6 + 10 ,M = 2e6 ;vector<int >idx[N]; int ret[N],a[N],pre[N],cnt[N];int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int n,k; cin>>n>>k; int ans = 0 ; for (int i = 1 ;i <= n; i++) { cin>>a[i]; cnt[a[i]+M]++; ans = max (ans,cnt[a[i]+M]); } if (k==0 ){ cout<<ans<<"\n" ; return 0 ; } for (int i = 1 ;i <= n; i++) { ret[a[i]+M]--; if (ret[a[i]+M]<0 )ret[a[i]+M] = 0 ; ret[a[i]+k+M]++; ans = max (ans,cnt[a[i]+k+M]+ret[a[i]+k+M]); } cout<<ans<<"\n" ; return 0 ; }
思想:边扫边统计答案,无需先确定区间再去算。
J. Xingqiu’s Joke 思路:对于同时 和同时 的操作,两数的差值的不变的。 。
若 且 那么 。
表示从 得到 的步数。我们去枚举 的质因数 。
而 可以由 和 转移得到。我们去记忆化一下就可以写了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <bits/stdc++.h> using namespace std;typedef long long ll;#define ll int const int mod = 1e9 + 7 ;const int N = 2e5 + 10 ;vector<ll>d; void div (ll x) { for (ll i = 2 ; i <= x / i; i++) { if (x % i == 0 ) { int s = 0 ; while (x % i == 0 ) { x = x / i; s++; } d.push_back (i); } } if (x > 1 )d.push_back (x); } map<pair<ll,ll>,ll>dp; ll dfs (ll a,ll c) { if (a==1 )return 0 ; if (c==1 )return a-1 ; ll &res = dp[{a,c}]; if (res)return res; res = a-1 ; for (auto g : d) { if (c % g == 0 ) { int left = a%g; ll t1 = left+1 +dfs (a/g,c/g); ll t2 = (g-left)+1 +dfs (a/g+1 ,c/g); res = min ({res,t1,t2}); } } return res; } int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int t; cin>>t; while (t--) { ll a,b; cin>>a>>b; d.clear (); dp.clear (); if (a>b)swap (a,b); ll c = abs (a-b); div (c); cout<<dfs (a,c)<<"\n" ; } return 0 ; }
M. Windblume Festival 思路:分类讨论。
有正有负。用负数一直减正数。最后用正数减负数这样最优。结果是
全正。先变出一个负数之后,变成第一种情况。
全负。先变出一个正数之后,变成第一种情况。
因为 不知道变哪个,需要 一遍。
注意:特判 的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int mod = 1e9 + 7 ;const int N = 1e6 + 10 ;ll a[N]; int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int t; cin>>t; while (t--) { int n; cin>>n; int cntn = 0 ,cntp = 0 ; ll sum = 0 ; for (int i = 0 ;i < n; i++) { cin>>a[i]; sum += abs (a[i]); if (a[i]<0 )cntn++; else if (a[i]>0 )cntp++; } if (n==1 ) { cout<<a[0 ]<<"\n" ; continue ; } ll ans = -1e18 ; if (cntn>0 &&cntp>0 ) { ans = sum; } else if (cntn) { for (int i = 0 ;i < n; i++) { if (a[i]-a[(i+1 )%n]>=0 ) ans = max (ans,(a[i]-a[(i+1 )%n])+sum+a[i]+a[(i+1 )%n]); } } else if (cntp) { for (int i = 0 ;i < n; i++) { if (a[i]-a[(i+1 )%n]<=0 ) ans = max (ans,sum-(a[i]-a[(i+1 )%n])-a[i]-a[(i+1 )%n]); } } else ans = 0 ; cout<<ans<<"\n" ; } return 0 ; }