Educational Codeforces Round 103 (Rated for Div. 2)
Educational Codeforces Round 103 (Rated for Div. 2)
A. K-divisible Sum
Problem:
给定 n 和 k,请问使 n 个正整数的和可以整除 k 时,n 个数中最大的数最小是多少?
(a1 + a2 + a3 + .... + an) % k == 0 时,使max(a1,a2,a3,....,a4) 最小
Solution:
很明显,n 个数最小能的和就是 n ,所以首先我们需要求出 k * i >= n 时最小的 i 是多少,然后让 (k * i / n)向上取整就是答案。(要让最大的数最小,很明显就是将需要求得的和尽可能被平分,要是不能平分则将会导致最大的数比能够平分的数大 1 )(n = 4,k * i = 10,10 = 2 + 2 + (2 + 1) + (2 + 1))
#include<bits/stdc++.h> #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define _for(i,s,t) for(int i=s;i<t;i++) #define _rof(i,s,t) for(int i=s;i>t;i--) #define rep(i,s,t) for(int i=s;i<=t;i++) #define per(i,s,t) for(int i=s;i>=t;i--) #define Ri(x) scanf("%d",&x) #define Rii(x,y) scanf("%d%d",&x,&y) #define INF 0x3f3f3f3f using namespace std; template<class T>inline void read(T &res) { char c;T flag=1; while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0'; while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag; } typedef long long ll; const int maxn = 1e5 + 10; int main(){ IOS; int t,n,k; cin>>t; while(t--){ cin>>n>>k; if(n > k){ k = ceil(n*1.0 / k) * k; } if(n == 1){ cout<<k<<endl; continue; } cout<<(int)ceil(k*1.0/n)<<endl; } return 0; }
B. Inflation
Problem:
给的 n 和 k,有 n 个数 p0,p1,p2,p(n-1),问在允许对 pi 进行增加时,如何在能保证(p[ i ] /(p[ 1 ] + ..... + p[ i - 1 ])<= 1 / k)的情况下, 最少需要增加 pi 多少值。
Solution:
我们从头计算,遇到不满足式子的就增加其分母到刚好满足的情况就好了,最终计算每次增加的和就是答案。注意在计算增加多少时可能无法让式子两边一定相等,所以需要对其进行向上取整,保证一定满足小于等于。
#include<bits/stdc++.h> #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define _for(i,s,t) for(int i=s;i<t;i++) #define _rof(i,s,t) for(int i=s;i>t;i--) #define rep(i,s,t) for(int i=s;i<=t;i++) #define per(i,s,t) for(int i=s;i>=t;i--) #define Ri(x) scanf("%d",&x) #define Rii(x,y) scanf("%d%d",&x,&y) #define INF 0x3f3f3f3f using namespace std; template<class T>inline void read(T &res) { char c;T flag=1; while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0'; while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag; } typedef long long ll; const int maxn = 1e2 + 10; ll p[maxn]; int main(){ IOS; ll t,n,k; cin>>t; while(t--){ cin>>n>>k; rep(i,1,n){ cin>>p[i]; p[i] = p[i]; } ll sum = p[1],ans = 0,tmp = 0; rep(i,2,n){ if(p[i]*100 > k * 1ll * sum){ tmp = ceil(p[i]*1.0 * 100 / k - sum); ans += tmp; sum += tmp; } sum += p[i]; } cout<<ans<<endl; } return 0; }
C. Longest Simple Cycle
Problem:
给定 n 条链,每一条链上面有 c[ i ] 个点(点的编号为 1 ~ c[ i ]),每一条链上面的第一个点会连接前一条边的 a[ i ] 点,最后一个点会连接前一条边的 b[ i ] 点,问最长的环是多长?(环是一个链,其中第一个顶点和最后一个顶点也是连接的。如果你沿着这个环运动,这个环的每个顶点都会恰好到达一次)
Solution:
由于每个链都是第一个点和最后一个点和前一条的点相连,所以第一条链是连接不到前面的,最后一条边是不会被别人连接的。
由此我们可以知道形成环会出现以下几种情况:
(1)相邻两条链构成环,那么长度就是 | a - b | + c + 2 (其中 a 和 b 是当前链连接前面链的点的编号,c 是当前链的长度,2 是连接时候的两条边)
(2)多条链构成环,由于是多条链,所以除去第一条和最后一条以外,中间的链所贡献的长度都是 min(a,b) - 1 + c - max(a,b) (a,b是被连接的点,c是当前边的长度)。不过由于我们可能会遇到丢弃前面多条链,只保留当前链,出现这种情况是因为前面多条链贡献的长度小于当前链的 | a - b | 的长度。(注意:在讨论时,我们都是以保留了我们当前多条链中的最后一条链为前提,这样才能保证可以形成环)
编程:首先第一条链的贡献值为 | a - b |,然后我们从第二条开始往后遍历,遍历到第 i 条链时。
若 ai == bi,则表示我们需要丢弃前面计算出的长度,将当前环的长度变成 2 ,因为当前第 i 条链连接了第 i - 1 条链的同一个点;
若 ai != bi ,设当前未连接第 i 条链时,当前环的长度为 res,当前最大的答案为ans,那么我们需要更新
res = max(res + a[ i ] - 1 + c[ i - 1 ] - b[ i ],| a[ i ] - b[ i ] |);
(res + a[ i ] - 1 + c[ i - 1 ] - b[ i ]):表示的是环继续沿着链走,| a[ i ] - b[ i ] |:表示的是环丢弃前面的链,只保留第 i - 1 条链。
ans = max(ans,res + c[ i ] - 1 + 2);
(res + c[ i ] - 1 + 2):表示以当前链为最后一条链形成环时的长度
#include<bits/stdc++.h> #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define _for(i,s,t) for(int i=s;i<t;i++) #define _rof(i,s,t) for(int i=s;i>t;i--) #define rep(i,s,t) for(int i=s;i<=t;i++) #define per(i,s,t) for(int i=s;i>=t;i--) #define Ri(x) scanf("%d",&x) #define Rii(x,y) scanf("%d%d",&x,&y) #define INF 0x3f3f3f3f using namespace std; template<class T>inline void read(T &res) { char c;T flag=1; while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0'; while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag; } typedef long long ll; const int maxn = 1e5 + 10; ll a[maxn],b[maxn],c[maxn]; int main(){ IOS; int t,n; cin>>t; while(t --){ cin>>n; rep(i,1,n){ cin>>c[i]; } rep(i,1,n){ cin>>a[i]; } rep(i,1,n){ cin>>b[i]; } if(a[1] > b[1]) swap(a[1],b[1]); ll ans = 0,res = 0; rep(i,2,n){ if(a[i] > b[i]) swap(a[i],b[i]); if(a[i] == b[i]){ ans = max(ans,res); ans = max(ans,c[i] - 1 + 2); res = 0; }else{ if(i - 1 == 1){ res += b[i] - a[i]; }else{ res += a[i] - 1 + c[i - 1] - b[i]; res = max(res,b[i] - a[i]); } ans = max(ans,res + c[i] - 1 + 2); } res += 2; } res += c[n] - 1; ans = max(ans,res); cout<<ans<<endl; } return 0; }
D. Journey
Problem:
给你长度为 n 的字符串s,字符串中只有 L 和 R 两种字符,s[ i ] = 'L' 表示存在一条 i + 1 到 i 的边,s[ i ] = 'R' 表示存在一条 i 到 i + 1 的边。当一个人从一个点到另一个点后,所有的边的方向都会反向一次,问从每个点出发,最多能到达几个点(可以经过一个点多次)。
Solution:
先计算每个点只能向左走能经过几个点,然后计算每个点只能向右走能经过几个点,最终答案就是向左的加上向右的。
设 num[ i ] = j 表示第 i 个点能到达几个点,初始化所有值为 1(此处需要定义 numl 和 numr 两个数组)
计算每个点只能向左走能经过几个点,我们从前往后遍历这 n + 1 个点(从 2 遍历到 n + 1),若 i 到 i - 1 有边的时候,我们numr[ i ] += 1,若 (i 到 i - 1有边 && i - 2 到 i - 1有边)则numr[ i ] += numr[i - 2]
计算每个点只能向右走能经过几个点,我们从后往前遍历(从 n 遍历到 1),若 i 到 i + 1 有边的时候,我们numl[ i ] += 1,若 (i 到 i + 1有边 && i + 2 到 i + 1有边)则numl[ i ] += numl[i + 2]
最终numl[ i ] + numr[ i ] - 1就是第 i 个点的答案。
#include<bits/stdc++.h> #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define _for(i,s,t) for(int i=s;i<t;i++) #define _rof(i,s,t) for(int i=s;i>t;i--) #define rep(i,s,t) for(int i=s;i<=t;i++) #define per(i,s,t) for(int i=s;i>=t;i--) #define Ri(x) scanf("%d",&x) #define Rii(x,y) scanf("%d%d",&x,&y) #define INF 0x3f3f3f3f using namespace std; template<class T>inline void read(T &res) { char c;T flag=1; while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0'; while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag; } typedef long long ll; const int maxn = 3e5 + 10; int numr[maxn],numl[maxn]; char s[maxn]; int main(){ IOS; int t,n; cin>>t; while(t --){ cin>>n; cin>>(s + 1); numr[1] = 1; rep(i,2,n + 1){ numr[i] = 1; if(s[i - 1] == 'L') numr[i] += 1; if(i - 2 >= 1 && s[i - 1] == 'L' && s[i - 2] == 'R') numr[i] += numr[i - 2]; } numl[n + 1] = 1; per(i,n,1){ numl[i] = 1; if(s[i] == 'R') numl[i] += 1; if(i + 1 <= n && s[i] == 'R' && s[i + 1] == 'L') numl[i] += numl[i + 2]; } rep(i,1,n + 1){ cout<<(numr[i] + numl[i] - 1)<<" "; } cout<<endl; } return 0; }
补题 D
E、F、G 略