Loading

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;
}
posted @ 2022-04-18 20:19  ZCETHAN  阅读(133)  评论(0编辑  收藏  举报