Educational Codeforces Round 188(CF2204)
A. Passing the Ball
模拟记录一下即可。
点击查看代码
//像太阳半沉在海水里,光和浪潮交织出了浓稠的雾。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
char c[55];
bool vs[55];
void xm(){
memset(vs,0,sizeof(vs));
cin >> n;
for(int i = 1;i <= n;++ i)
cin >> c[i];
int w = 1;vs[1] = 1;
int gs = 1;
while(n--){
if(c[w] == 'R') w++;
else w--;
if(!vs[w]) gs++,vs[w] = 1;
}
cout << gs << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int _;
cin >> _;
while(_--) xm();
return 0;
}
B. Right Maximum
模拟。
做一个前缀\(max\)数组。
对于每一次操作(以下\(n\)表示当前操作时序列的长度而非原序列长度),取出\(1\) ~ \(n\)的最大值,然后从\(n\)开始倒着枚举每一个数,找到最后一个等于这个最大值的位置\(w\),那么这一次就会把\(w\) ~ \(n\)删去,让\(n = w-1\)即可。
模拟的同时记录进行了几次操作即可。
因为我们每次操作只会枚举要删去的部分,所以总复杂度是\(O(n)\)。
点击查看代码
//像太阳半沉在海水里,光和浪潮交织出了浓稠的雾。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int c[200005];
int qzh[200005];
void xm(){
cin >> n;
for(int i = 1;i <= n;++ i)
cin >> c[i],qzh[i] = max(qzh[i-1],c[i]);
int gs = 0;
while(n != 0){
int w = qzh[n];
while(c[n] != w) n--;
n--;gs++;
}
cout << gs << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int _;
cin >> _;
while(_--) xm();
return 0;
}
C. Spring
简单容斥一下。
加上本身,这时两两之间\(lcm\)的会多算\(3\),减去两两之间的\(lcm\)乘\(3\),这时三个的\(lcm\)会被减没,也就是少算了\(2\),再加上三个的\(lcm\)乘\(2\)即可。
点击查看代码
//像太阳半沉在海水里,光和浪潮交织出了浓稠的雾。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,b,c,m;
int gcd(int x,int y){
if(y == 0) return x;
return gcd(y,x%y);
}
int lcm(int x,int y){
return x/gcd(x,y)*y;
}
void xm(){
cin >> a >> b >> c >> m;
int ags = 0,bgs = 0,cgs = 0;
ags += 6*(m/a);
bgs += 6*(m/b);
cgs += 6*(m/c);
ags -= 3*(m/lcm(a,b))+3*(m/lcm(a,c));
bgs -= 3*(m/lcm(b,a))+3*(m/lcm(b,c));
cgs -= 3*(m/lcm(c,a))+3*(m/lcm(c,b));
int g = lcm(a,b);g = lcm(g,c);
ags += 2*(m/g);
bgs += 2*(m/g);
cgs += 2*(m/g);
cout << ags << ' ' << bgs << ' ' << cgs << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int _;
cin >> _;
while(_--) xm();
return 0;
}
D. Alternating Path
发现如果有边数为奇数的环那么这整个联通块的所有点就都不可能是完美节点了。
我们进行一个\(dfs\),有返祖边的时候就判断一下是否为奇环,如果整个联通块都没有奇环就统计答案。
答案即遍历出来的\(dfs\)树的奇数层节点个数和偶数层节点个数的\(max\)。因为你可以换个根,那么偶数层就变成了奇数层,奇数层就变成了偶数层,所以我们就只考虑为什么答案是奇数层节点个数。容易证明,若令所有的边都从奇数层连向偶数层,那么奇数层的所有节点一定为完美节点,而偶数层所有节点一定不为完美节点。(由于没有奇环,所有的返祖边一定是连接了一个奇数层节点和一个偶数层节点。)
点击查看代码
//像太阳半沉在海水里,光和浪潮交织出了浓稠的雾。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
vector<int> ed[200005];
int dep[200005];
int gs;
int g0;
bool dfs(int x,int fa){
gs++;
dep[x] = dep[fa]+1;
if(dep[x]&1) g0++;
bool yj = 1;
for(auto y:ed[x]){
if(y == fa) continue;
if(!dep[y]) yj &= dfs(y,x);
else{
if((dep[x]-dep[y]+1)&1) yj = 0;
}
}
return yj;
}
void xm(){
cin >> n >> m;
for(int i = 1;i <= m;++ i){
int u,v;
cin >> u >> v;
ed[u].push_back(v);
ed[v].push_back(u);
}
int ans = 0;
for(int i = 1;i <= n;++ i){
if(!dep[i]){
gs = 0;g0 = 0;
if(dfs(i,0)) ans += max(g0,gs-g0);
}
}
cout << ans << '\n';
for(int i = 1;i <= n;++ i)
dep[i] = 0,ed[i].clear();
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int _;
cin >> _;
while(_--) xm();
return 0;
}
E. Sum of Digits (and Again)
定义\(f(x)\)代表将\(x\)各个位相加的和。
我们发现即使\(S(x)\)中的\(x\)有\(10^5\)位,那么\(f(x)\)也会\(\leq 9 \times 10^5\),\(f(f(x)) \leq 36\),\(f(f(f(x))) \leq 9\),就变成一位数了。
也就是说,如果我们枚举\(f(x)\),可以很快的判断\(S(x)\)是否可以通过重排字符串得到。
具体怎样判断呢?我们可以预处理出\(\forall 1 \leq x \leq 9 \times 10^5\),\(S(x)\)会用到每个数字各几个,之后,将判断分为两步。首先,判断字符串每个数字个数是否都大于等于\(S(x)\)用到的数字个数,如果有小于的,则一定不行。其次,拼完\(S(x)\)后剩下的数字的加和是否等于\(x\),若相等,将剩下的数字排在前面,进行一次\(f\)后一定会变成\(x\),然后再进行\(S(x)\),刚好用完所用字符,否则就不行。
输出随便做一下。
点击查看代码
//像太阳半沉在海水里,光和浪潮交织出了浓稠的雾。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int gs[15];
int cnt[900005][15];
string c;
bool check(int x){
int s = 0,t = 0;
for(int i = 0;i < 10;++ i){
if(gs[i] < cnt[x][i]) return 0;
s += (gs[i]-cnt[x][i])*i;
t += gs[i]-cnt[x][i];
}
return (!t||s == x);
}
void xm(){
cin >> c;
n = c.length();
if(n == 1){
cout << c << '\n';
return;
}
memset(gs,0,sizeof(gs));
for(int i = 0;i < n;++ i)
gs[c[i]-'0']++;
int p = 0;
for(int i = 2;i <= 900000;++ i){
if(check(i)){
p = i;
break;
}
}
for(int i = 10;i >= 0;-- i){
int g = gs[i]-cnt[p][i];
while(g--) cout << i;
}
while(1){
if(p < 10){
cout << p;
break;
}
cout << p;
int s = 0;
while(p){
s += p%10;
p /= 10;
}
p = s;
}
cout << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
for(int i = 2;i <= 900000;++ i){
int x = i;
while(1){
if(x < 10){
cnt[i][x]++;
break;
}
int s = 0;
while(x){
cnt[i][x%10]++;
s += x%10;
x /= 10;
}
x = s;
}
}
int _;
cin >> _;
while(_--) xm();
return 0;
}
F. Sum of Fractions
这是个有意思的题。
首先我们来考虑这个\(MSF(b,k)\)是什么东西。
可以发现,我们对最大的分数(也即分母最小的分数)进行\(k\)次增值操作是最优的。(证明自己想,总的来说就是每一步都要贪心使增加的数最大,可以拿\(\frac{1}{3}\)和\(\frac{1}{4}\)举个例子看看)
那么问题变成了有一个分数\(\frac{1}{x}\),对它进行\(k\)次增值最大为多少。
1.\(k < x\)
将\(\frac{1}{x}\)变为\(\frac{k+1}{x}\)
2.\(k \geq x\)
将\(\frac{1}{x}\)变为\(\frac{1+k-x+1}{1}\),也就是\(k-x+2\)。
证明略,感性理解一下吧。
我们记将分数\(\frac{1}{x}\)增值\(k\)次后的最大值为\(f(x,k)\)。
好咯,\(MSF(b,k)\)被我们成功地解决了。
那么考虑那个很长地式子怎么求。先考虑\(m=1\)的情况吧。
可以发现,一个区间有用的数只有那个最小的,那么我们求两个数组,\(pre_i\)代表\(i\)前面第一个小于等于它的,\(nxt_i\)代表\(i\)后面第一个小于它的,那么\(i\)作为最小数的区间\([l,r]\)将会满足\(pre_i+1 \leq l \leq i\)且\(i \leq r \leq nxt_i-1\),剩下的区间,也就是\(i\)不为最小值的区间,一共有\(i \times (n-nxt_i+1) + pre_i \times (n-i+1) - pre_i \times (n-nxt_i+1)\)个。(划重点!!!不是\(pre_i \times (n-nxt_i+1)\)个!!!)那么对于这一部分区间,\(i\)将只会贡献一个\(\frac{1}{a_i}\),总共贡献$\frac{1}{a_i} \times $ 区间个数(也就是上面那个式子)。
那对于\(i\)作为最小数的那些区间,\(i\)将会贡献\(f(a_i,k) \times (i-pre_i) \times (nxt_i-i)\)。
这样,我们处理好了\(m=1\)的情况,那么对于\(m \neq 1\)怎么办呢?
每一个\(i\),对于那些只贡献一个\(\frac{1}{a_i}\)的区间,每个\(k\)是没区别的,相当于对答案进行全局加,记一个变量就行,好做。
那么剩下的呢?其实都是要加上\(f(a_i,k_x) \times (i-pre_i) \times (nxt_i-i)\)的。
设\(\forall 1 \leq x \leq w k_x < a_i\)且\(\forall w+1 \leq x \leq m k_x < a_i\)(也就是说\(w\)是\(k\)是否小于\(a_i\)的分界点),\(sum = (i-pre_i) \times (nxt_i-i)\)(\(sum\)就是能产生贡献的区间个数)。
对于\(k_x < a_i\)的那一部分\(k_x\),加上的是\(\frac{k_x+1}{x} \times sum\),就是\((k_x+1) \times \frac{1}{x} \times sum\)。
对于\(k_x \geq a_i\)的那一部分\(k_x\),加上的是\((k_x-a_i+2) \times sum\),就是\((k_x+2) \times sum - a_i \times sum\)。
可以发现,不管是哪一部分,加上的数都可以分成只和\(k_x\)有关,和只和\(a_i\)有关的两部分,但是一个是乘上\(k_x+1\),一个是加上\(k_x+2\)。
开两个答案数组,一个维护\(k_x < a_i\)的那一部分\(a_i\)对\(k_x\)的答案的贡献,一个维护\(k_x \geq a_i\)的那一部分\(a_i\)对\(k_x\)的答案的贡献。
也就是,对于第一个答案数组,我们要将\(1\) ~ \(w\)加上\(\frac{1}{x} \times sum\),对于第二个答案数组,我们要将\(w+1\) ~ \(m\)加上\(-a_i \times sum\)。都是区间操作,用差分维护。
最终将第一个答案数组的每一个数乘上\(k_x+1\),将第二个答案数组的每一个数加上\((k_x+2) \times\) 每个对这个位置有贡献的\(i\)的\(sum\)的和。
“每个对这个位置有贡献的\(i\)的\(sum\)的”怎么求呢?新开一个数组,每次要对于\(w+1\) ~ \(m\)的位置加上\(sum\),依旧用差分维护。
至此,此题结束。
点击查看代码
//像太阳半沉在海水里,光和浪潮交织出了浓稠的雾。
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
int n,m;
int a[500005];
int k[500005];
int pre[500005];//<=
int nxt[500005];//<
int ksm(int a,int b){
if(b == 0) return 1;
int c = ksm(a,b/2);
c *= c;c %= mod;
if(b%2) c *= a;
return c%mod;
}
int ansz;
int ans1[500005];
int ans2[500005];
int gs[500005];
int st[500005],sttp;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin >> n >> m;
for(int i = 1;i <= n;++ i)
cin >> a[i];
for(int i = 1;i <= m;++ i)
cin >> k[i];
sttp = 0;
st[++sttp] = 0;
for(int i = 1;i <= n;++ i){
while(sttp&&a[st[sttp]] > a[i]) sttp--;
pre[i] = st[sttp];
st[++sttp] = i;
}
sttp = 0;
st[++sttp] = n+1;
for(int i = n;i >= 1;-- i){
while(sttp&&a[st[sttp]] >= a[i]) sttp--;
nxt[i] = st[sttp];
st[++sttp] = i;
}
for(int i = 1;i <= n;++ i){
int gs = i*(n-nxt[i]+1)%mod+pre[i]*(n-i+1)%mod;gs %= mod;
gs += mod-pre[i]*(n-nxt[i]+1)%mod;gs %= mod;
ansz += gs*ksm(a[i],mod-2)%mod;
}
for(int i = 1;i <= n;++ i){
int _ = (i-pre[i])*(nxt[i]-i)%mod;
int w = lower_bound(k+1,k+1+m,a[i])-k-1;
int f = ksm(a[i],mod-2)*_%mod;
ans1[1] += f;ans1[1] %= mod;
ans1[w+1] += mod-f;ans1[w+1] %= mod;
w++;
f = (mod-a[i])%mod*_%mod;
ans2[w] += f;ans2[w] %= mod;
gs[w] += _;gs[w] %= mod;
}
for(int i = 1;i <= m;++ i)
ans1[i] += ans1[i-1],ans1[i] %= mod,
ans2[i] += ans2[i-1],ans2[i] %= mod,
gs[i] += gs[i-1],gs[i] %= mod;
for(int i = 1;i <= m;++ i){
ans1[i] *= k[i]+1;ans1[i] %= mod;
ans2[i] += (k[i]+2)*gs[i]%mod;
ans2[i] %= mod;
}
for(int i = 1;i <= m;++ i)
cout << (ans1[i]+ans2[i]+ansz)%mod << '\n';
return 0;
}

浙公网安备 33010602011771号