Codeforces Round #782 (Div. 2)
前言
两个 E,两个印度人。
\(\texttt{Rating Change:}\color{grey}{683}\color{black}\to \color{green}{1205}\)
\(\Delta={\color{green}{\texttt{522}}}\qquad \texttt{rank:285}\)
A
由于题目保证了红色一定大于蓝色,所以直接算在 \(b+1\) 个空隙中把红色均匀插入就可以了。然后模拟一遍输出。
My Code
void solve(){
int n,r,b;cin>>n>>r>>b;
int num=r/(b+1);
int rem=r%(b+1);
if(0<rem){
rep(j,1,num+1) cout<<"R";
}else{
rep(j,1,num) cout<<"R";
}
rep(i,1,b){
cout<<"B";
if(i<rem){
rep(j,1,num+1) cout<<"R";
}else{
rep(j,1,num) cout<<"R";
}
}cout<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;for(cin>>T;T--;) solve();
return 0;
}
B
首先讨论 \(k\) 的奇偶性。如果是奇数,我们可以考虑把序列 01 翻转来把 \(k\) 变成偶数,这是合法的。接下来,每次对一个数操作相当于在最后的时候是翻转了它的二进制位的,所以从高到低翻转 \(0\) 就可以了。最后的时候如果 \(k\) 还有多,那就疯狂翻最低位一定是最优的。
My Code
const int MAXN=2e5+10;
int cnt[MAXN];
void solve(){
int n,k;cin>>n>>k;
rep(i,1,n) cnt[i]=0;
string b;cin>>b;
if(k&1) rep(i,0,n-1) b[i]=(b[i]=='1'?'0':'1');
rep(i,0,n-2) if(b[i]=='0'&&k) k--,b[i]='1',cnt[i+1]++;
cnt[n]=k;if(k&1) b[n-1]=(b[n-1]=='1'?'0':'1');
cout<<b<<'\n';
rep(i,1,n) cout<<cnt[i]<<' ';cout<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;for(cin>>T;T--;) solve();
return 0;
}
C
给你一个数轴上若干个点,你的起点初始为 \(0\),每个点初始是白色的,然后你需要把每个点染黑,代价是 \(b\times |s-t|\)。或者你可以改变你的起点到任意一个黑色的点,代价是 \(a\times |s-t|\)。求最终每个点都染黑的最小代价。
最直观的感受一定是你先染第一个点,然后把这个点定为你的起点。考虑什么情况会比这样子做更优。就是我们一定是到某一个位置就不改起点了,那么后面的点就全部要通过重新跑一遍来染色。预处理一下就可以啦。
My Code
const int MAXN=2e5+10;
int x[MAXN],sum[MAXN],cur[MAXN];
void solve(){
int n,a,b;cin>>n>>a>>b;
rep(i,1,n) cin>>x[i],sum[i]=cur[i]=x[i]-x[i-1],sum[i]=sum[i]*(a+b);
rep(i,2,n) sum[i]+=sum[i-1];int now=0,ans=INF;
per(i,n,1) now+=cur[i]*(n-i+1)*b,ans=min(ans,now+sum[i-1]);
cout<<ans<<'\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;for(cin>>T;T--;) solve();
return 0;
}
D
首先容易想到,\(a\) 的和除以 \(n\) 就是 \(b\) 中 \(1\) 的个数对吧。如果我们确定当前位,那么我们就能知道前缀中 \(1\) 的数量是怎么变的。接下来,我们在后缀的 \(k\) 个中减去 \(1\),表示最后一次排序给序列带来的结果。然后我们每次考察一个位置是否等于 \(0\) 就能知道当前位是 \(0\) 还是 \(1\) 了。一个后缀中减去可以转化成前缀加然后判断是否和 \(a\) 相等,树状数组即可。
My Code
const int MAXN=2e5+10;
struct BIT{
int tr[MAXN],n;
void init(int len){rep(i,0,len) tr[i]=0;n=len;}
int lbt(int x){return x&(-x);}
void upd(int x,int v){for(;x<=n;x+=lbt(x))tr[x]+=v;}
int ask(int x){int ret=0;for(;x;x-=lbt(x))ret+=tr[x];return ret;}
}Tr;
int a[MAXN],ans[MAXN];
void solve(){
int n,sum=0;cin>>n;
rep(i,1,n) cin>>a[i],sum+=a[i];
Tr.init(n);sum/=n;Tr.upd(n-sum+1,1);
per(i,n,2){
int cur=Tr.ask(i);
if(cur^a[i]) ans[i]=1,sum--;
else ans[i]=0;
if(i^sum) Tr.upd(i-sum,1);
}ans[1]=sum;
rep(i,1,n) cout<<ans[i]<<' ';cout<<'\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;for(cin>>T;T--;) solve();
return 0;
}
E
神奇的简单题/qd。容易发现答案只能是 \(0\) 或者 \(1\) 或者 \(2\)。然后考虑 \(0\) 的情况,你对每个二进制位维护一个并查集然后查连通性就可以了。考虑 \(1\),既然不是 \(0\),那我们就只要判断中间会不会出现 \(1\)。也很好做,你考虑路径一定是这样的:先是一段与和为奇数的非 \(1\),然后是一段与和为偶数的,由于 \(mex\) 不是 \(0\),所以这样一定会出现 \(0\) 而不出现 \(1\)。所以我们需要维护联通块是边权为奇数的并且 \(i\) 位都是 \(1\) 的,并查集即可。
My Code
const int MAXN=1e5+10;
int ok[MAXN];
struct dsu{
int f[MAXN];
int find(int x){while(x^f[x])x=f[x]=f[f[x]];return x;}
void merge(int x,int y){if(find(x)^find(y))f[find(x)]=find(y);}
}D[35],C[35];
int sok[35][MAXN];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,m;cin>>n>>m;
rep(i,1,n) rep(b,0,30) D[b].f[i]=i,C[b].f[i]=i;
rep(i,1,m){
int u,v,w;cin>>u>>v>>w;
rep(b,0,30) if(w&(1<<b)) D[b].merge(u,v);
if(w%2==0) ok[u]=ok[v]=1;
else rep(b,1,30) if(w&(1<<b)) C[b].merge(u,v);
}
rep(i,1,n) if(ok[i]) rep(b,1,30)
sok[b][C[b].find(i)]=1;
int Q,u,v;cin>>Q;
while(Q--){
cin>>u>>v;
int ans=2;
rep(b,0,30)
if(D[b].find(u)==D[b].find(v))
{ans=0;break;}
if(ans==2){
rep(b,1,30)
if(sok[b][C[b].find(u)])
{ans=1;break;}
}
cout<<ans<<'\n';
}
return 0;
}
F
牛逼博弈,不会。
下面是题解的思想。
我们首先来证明:如果有一条长度大于 \(2\) 的链,那么 Alice 将胜利。为此,我们需要证明一些引理。
- lemma 1:最后的状态总是两个数不在本来的位置上。此时如果 Bob 的棋子在这两个数中的其中一个,那么我们继续博弈,否则直接交换这两个数就胜利了。证明:如果当前有大于等于 \(3\) 个数不在自己的位置上,我们根据置换群的一些知识,总是可以找到一个点能换到自己的位置上。
- lemma 2:在上面的前提下,我们总是可以在没有胜利的时候,把两个数移动到树上相邻的任意两个位置。证明:(下面用加粗表示棋子所在)假设我们当前不在自己位置上的数是 AB。然后棋子必然是在 A 或 B 上的,假设在 A 上,C 是 AB 之间的某个点。首先容易证明,C 是 AB 之间唯一的点的情况是可以推广的。于是我们直接考虑一条链上 ACB,然后我们现在 AB 的位置是反的。然后我们交换 BC,这样 Bob 只能选择 ABC。然后交换 AC。
- 如果 Bob 变成 CBA,那么交换 AB,然后 Bob 无论选择什么,我们还是可以交换 BC 变成 BAC。此时我们发现当前的 B 回到了自己的位置,现在不在自己位置上的数字变成了 AC。成功贴在了一起。
- 如果 Bob 变成 CBA,那么直接交换 BC,就达到了目的。
- lemma 3:我们总是可以把连在一起的两个不在一起的棋子移动到任意位置,或者像题解说的,总是可以「跳过」一个节点。
通过这个,我们可以把情况简化成:在一条长度大于 \(2\) 的一端,我们有两个连在一起的节点希望能交换它们。
现证明总是可以获胜:假设我们最终希望它变成这样一条链:ABCD。然后我们现在是 BACD。如果棋子在 A 上,可以通过 lemma 3 把 B 跳到 C,然后把 A 跳到 D,就变成了棋子在端点的情况了。然后我们交换 AD,此时变成 BDCA,然后交换 AB,变成 ADCB 或者 ADCB,无论怎么样,都是可以交换 BD,然后就赢了。
于是我们只需要考虑直径小于等于 \(2\) 的情况,不难发现,这就是一张 Star-Graph。首先我们判断在第一步之内,Alice 能不能取胜。
接下来让我们定义:\(d=\) 最少的交换次数,\(x=\) 当前棋子是否在叶子上。然后可以发现,\(d+x\) 的奇偶性不变。
接下来我们讨论所有的六种最终情况:
- Case 1:中心不在自己位置上,棋子在中心。
容易构造出 Bob 胜利的策略。 - Case 2:棋子在一个回到自己位置上的叶子,中心没有回到自己的位置。
Alice 赢了。 - Case 3:棋子在一个没有回到自己位置上的叶子,中心也没有回到自己的位置。
第一步把中心任意移动,然后变成了 Case1,是必输态,Bob 胜。 - Case 4:中心在自己位置上,棋子在中心。
这样直接交换就 Alice 胜利了。 - Case 5:棋子在一个回到自己位置上的叶子,中心回到自己的位置。
Alice 赢了。 - Case 6:棋子在一个没有回到自己位置上的叶子,中心回到自己的位置。
显然是必败态。
接下来考虑初始状态怎么判断。首先考虑 Case 2&5 不可能存在超过 \(1\) 轮,否则 Bob 不可能傻到把棋子移到已经回去了的棋子。然后你发现 Case 1&2 也可以被避免,只要在第一步中,我们把中心移动到它本来的位置就好了。那如果移动不了,就是必败态了。
所以我们有如下策略:
棋子在中心并且中心没有回到原位。显然是必败态。Case 3。
棋子在中心并且中心已经回到原位了。此时我们判断 \(d+x\) 的奇偶性就好了。
棋子在叶子并且中心没有回到原位。如果我们没有办法使得中心回到原位,那就是必败了,否则还是通过判断奇偶可以得到答案。
棋子在叶子并且中心已经回到原位了。同样判断奇偶性就可以了。
My Code
const int MAXN=2e5+10;
vector<int> e[MAXN];
int dep[MAXN],son[MAXN];
void dfs(int x,int fa){
son[x]=x;
for(int s:e[x]){
if(s==fa) continue;
dep[s]=dep[x]+1; dfs(s,x);
if(dep[son[s]]>dep[son[x]])
son[x]=son[s];
}
}
int p[MAXN],vis[MAXN];
void solve(){
int n,x;cin>>n>>x;
rep(i,1,n-1){
int a,b;cin>>a>>b;
e[a].pb(b);e[b].pb(a);
}
int dif=0,df=0;
rep(i,1,n){
cin>>p[i],dif+=(p[i]!=i);
if(p[i]!=i) df=i;
}
if(dif==0||(dif==2&&x!=df&&x!=p[df])) cout<<"Alice\n";
else{
dfs(1,0);int rt=son[1];
rep(i,1,n) dep[i]=0;dfs(rt,0);
int Dia=dep[son[rt]];
if(Dia>=3) cout<<"Alice\n";
else if(Dia==1) cout<<"Alice\n";
else{
int cen;
rep(i,1,n) if((int)e[i].size()>1){cen=i;break;}
int d=0;
rep(i,1,n){
if(vis[i]) continue;
int now=i,cnt=0;
while(!vis[now]) cnt++,vis[now]=1,now=p[now];
d+=cnt-1;
}d+=(x!=cen);d%=2;
if(p[cen]==cen) cout<<(d?"Alice\n":"Bob\n");
else if(x==cen) cout<<"Bob\n";
else{
if(x==p[cen]) cout<<"Bob\n";
else cout<<(d?"Alice\n":"Bob\n");
}
}}
rep(i,1,n) e[i].clear(),vis[i]=0,dep[i]=0;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;for(cin>>T;T--;) solve();
return 0;
}