2020牛客NOIP赛前集训营-普及组(第五场)
A T1 购物
分析
由于是买 送一,所以我们考虑买
只需要
次花钱的购买。那么可以把
个物品分成很多段,每段的长度为
,然后处理一下边角就好了。
代码
#include<bits/stdc++.h> using namespace std; int read() {int x;scanf("%d",&x);return x;} int solve(int a,int b,int c) { return (a / (b + 1) * b + (a % (b + 1))) * c; } int main() { int T = read(); while(T--) { int n,k,x; n = read();k = read();x = read(); cout << solve(n,k,x) << endl; } }
B T2 交换
分析
对于环的分析,我们习惯于破环成链。这样在一个序列上也可以表示所有环了。那么我们就在这个长度为 的序列上找到最长的全为
的子串就好了。需要处理一下子串的长度大于
的情况。
代码
#include<bits/stdc++.h> using namespace std; int read() {int x;scanf("%d",&x);return x;} const int N = 210000; char ch[N]; int main() { scanf("%s",ch + 1); int n = strlen(ch + 1),ans = 0; for(int i = 1;i < n;i++) ch[i + n] = ch[i]; for(int i = 1,last = 0;i < n * 2;i++) { if(ch[i] == '1' && ch[i - 1] != '1') last = i; if(ch[i] == '1' && i - last + 1 <= n) ans = max(ans,i - last + 1); } cout << ans << endl; }
C T3 最少移动
分析
我们发现任意操作都不会改变 的,也就是总和不变。那么最后每个点的权值一定是
的,那么
的就可以输出
。因为要改变
就必须操作
所以我们可以从一个端点
扫一遍就好了。
代码
#include<bits/stdc++.h> using namespace std; int read() {int x;scanf("%d",&x);return x;} #define ll long long const int N = 1e5 + 100; int a[N];ll s; int main() { int T = read(); while(T--) { s = 0;int n = read(); for(int i = 1;i <= n;i++) { a[i] = read(); s += a[i]; } ll ans = 0; if(s % n) {printf("-1\n");continue;} for(int i = 1;i <= n;i++) { int sum = s / n - a[i]; a[i + 1] -= sum; ans += abs(sum); } cout << ans << endl; } }
D T4 飞行棋
分析
我们定义 为到节点
的期望回合数,那么
。由于
时每个节点只有
的概率转移到
剩下的
也会转移到另一个
的
节点。所以
时,每个
是相同的。
。那么当
时,直接转移就好了,用前缀和维护一下,时间复杂度为
。注意一下掷出来
时,并不改变回合数。
代码
#include<bits/stdc++.h> using namespace std; int read() {int x;scanf("%d",&x);return x;} #define ll long long const int N = 1e5 + 100; double f[N],s[N]; int main() { int T = read(); while(T--) { int n = read(),d = read();s[0] = f[0] = 1; for(int i = 1;i < d;i++) s[i] = d - 1,f[i] = f[i - 1] + s[i]; for(int i = d;i <= n;i++) { s[i] = (f[i - 1] - f[i - d] + d - 1) / (1.0 * d) + s[i - d] / (1.0 * d); f[i] = f[i - 1] + s[i]; } printf("%.2f\n",s[n]); } }