Codeforces Round #738 (Div. 2) 题解

A.Mocha and Math

A.Mocha and Math
题目大意:略
思路:首先一个数和另一个数做&操作结果不会变大,那么我们对于每一个 a i a_i ai直接让它异或整个序列即可,复杂度 O ( n 2 ) O(n^2) O(n2)

AC_Code:

#include<bits/stdc++.h>
using namespace std;
const int Maxn = 104;
int a[Maxn];
int main(){
	int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++){
        	for(int j=1;j<=n;j++){
        		a[i]&=a[j];
			}
		}
		int Max = 0;
		for(int i=1;i<=n;i++){
			Max = max(Max,a[i]);
		}
		cout<<Max<<'\n';
	}
	return 0;
}

B. Mocha and Red and Blue

B. Mocha and Red and Blue
题目大意:给你n个方块排成一行,R代表该方块为红色,B代表蓝色,?代表没有颜色,需要你填充,你只能用R和B对?进行填充,现在让你使用最优的填充方案,使得imperfectness最小,任意两个相邻的方块如果颜色一致则会对imperfectness贡献1.

思路:
给你的字符串有两种形式:
1.一种是全为?(方块都没被染色),这种情况只需要间隔填充颜色即可,例如RBRBRB…
2.一种是已有方块被染色,那么我们找道第一个被染色的方块的位置pos,先把区间 [ 1 , p o s − 1 ] [1,pos-1] [1,pos1]先填充完,再把区间 [ p o s + 1 , n ] [pos+1,n] [pos+1,n]填充完,填充方法贪心即可,如果旁边的是R那么我们就填B,反之则填R

AC_Code:

#include<bits/stdc++.h>
using namespace std;
const int Maxn = 105;
string s;
int main(){
	int t;
	cin>>t;
	while(t--){
		int n;
		cin>>n;
		cin>>s;
		int pos = -1;
		for(int i=0;i<n;i++) {
			if(s[i]!='?'){
				pos = i;
				break;
			}
		}
		if(pos==-1){
			for(int i=0;i<n;i++){
				if(i&1) s[i] = 'R';
				else s[i]='B';
			}
		}
		else{
		for(int i=pos-1;i>=0;i--){
			if(s[i+1]=='B') s[i] = 'R';
			else s[i] = 'B';
		}
		for(int i=pos+1;i<n;i++){
			if(s[i]=='?'){
				if(s[i-1]=='B') s[i]='R';
				else s[i]='B';
			}
		}
	    }
		cout<<s<<'\n';
	}	
	return 0;
}

C - Mocha and Hiking

C - Mocha and Hiking
题目大意:首先给你n+1个点,并且告诉你2n-1条有向边,你可以从任何一点出发,在任何一个点结束,问你能否走完所有n+1个点,并且每个点只走一次

思路:首先有4由题目给你的条件1可以直接到n,显然2也可以到直接到n,但是n不能直接到n+1,所以我们判断n能否直接到n+1,如果可以输出答案,如果不行,那么我们判断n+1能否到1如果可以输出答案,如果不行我就需要去找到一个点对满足,i能到n+1,并且n+1能到i+1的这样一个连续的点对(i,i+1),也就是是否存在一个连续的(0,1)点对,如果存在输出答案,如果没有,那么说明没有一个路径可以满足要求,输出-1

AC_code:

#include<bits/stdc++.h>
using namespace std;
const int Maxn = 1e4+10;
int a[Maxn];
int main(){
	int t;
	cin>>t;
	while(t--){
		int n;
		cin>>n;	
		for(int i=1;i<=n;i++) cin>>a[i];
		int flag = 0;
		if(a[n]==0){
			flag = 1;
			for(int i=1;i<=n+1;i++) cout<<i<<' ';
		}
		else if(a[1]==1){
			flag = 1;
			cout<<n+1<<' ';
			for(int i=1;i<=n;i++) cout<<i<<' ';
		}	    
		else{			
			for(int i=1;i<n;i++){
				if(a[i]==0&&a[i+1]==1){
					flag = 1;
					for(int j=1;j<=i;j++) cout<<j<<' ';
					cout<<n+1<<' ';
					for(int j=i+1;j<=n;j++) cout<<j<<' ';
					break;
				}
			}
		}
		if(!flag) cout<<-1;
		cout<<'\n';
	}
	return 0;
}

D1. Mocha and Diana (Easy Version)

D1. Mocha and Diana (Easy Version)
题目大意:给你两个森林,然后对两个森林的点进行连边(连边的过程两个森林是同步的,比如2和5连边,那么两个森林的2和5节点都要进行连边),要求连边之后依然为两个森林,问你最多可以连多少条边,注意连边只能是森林内部的节点进行.

思路:显然对于一个森林它的所有树是不可以出现环的(不然就不是树了),如果两个点属于同一棵树(也就是祖先一致)那么它们连边就会构成一个环,所以我们判定两个点是否可以连边只需要对于两个森林分别判定这两个点是否属于一棵子树即可,如果都满足就可以连边,两个点的选取和判定我们 n 2 n^2 n2暴力就行,并查集查询和合并操作复杂度logn,总复杂度 O ( n 2 l o g n ) O(n^2logn) O(n2logn)

AC_code:

#include<bits/stdc++.h>
using namespace std;
const int Maxn = 1e6+10;
typedef struct node{
	int u,v;
}Node;
Node ans[Maxn];
int size1[1005]={0};
int size2[1005]={0};
int fa1[1005]={0};
int fa2[1005]={0};
int n,m1,m2;
void init(){
	for(int i=1;i<=n;i++){
	fa1[i] = fa2[i] = i;
	size1[i] = size2[i] = 1;
	}
}
/*路径压缩,查找根节点*/
int find1(int x){return x==fa1[x]?x:fa1[x]=find1(fa1[x]);}
int find2(int x){return x==fa2[x]?x:fa2[x]=find2(fa2[x]);}
//合并操作 
void merge1(int x,int y){
	int xx=find1(x), yy=find1(y);
	/*矮数合并到高树上,缩短时间*/
	if(size1[xx]<size1[yy]) swap(xx,yy);
	fa1[yy]=xx;
	size1[xx]+=size1[yy];
	return ;
}
void merge2(int x,int y){
	int xx2=find2(x),yy2 = find2(y);
	if(size2[xx2]<size2[yy2]) swap(xx2,yy2);
	fa2[yy2]=xx2;
	size2[xx2]+=size2[yy2];
	return ;
}
int main(){
	
	cin>>n>>m1>>m2;
	init();
	for(int i=1;i<=m1;i++){
		int u,v;
		cin>>u>>v;
		merge1(v,u);
	}
	for(int i=1;i<=m2;i++){
		int u,v;
		cin>>u>>v;
		merge2(v,u);
	}
	int sum = 0;
	int p11=find1(2),p12 = find1(4); 
	for(int i=1;i<=n;i++)
	 for(int j=i+1;j<=n;j++){
	 	if((find1(i)!=find1(j))&&(find2(i)!=find2(j))){
	 		ans[++sum].u = i;
	 		ans[sum].v = j;
	 		merge1(i,j);
	 		merge2(i,j);
		}
	 }
	 cout<<sum<<'\n';
	 for(int i=1;i<=sum;i++){
	 	cout<<ans[i].u<<' '<<ans[i].v<<'\n';
	 }
	 return 0;
}

D2. Mocha and Diana (Hard Version)

D2. Mocha and Diana (Hard Version)
题目大意:同D2
思路:这道题的n达到了1e6,所以D1的复杂度是会T的,我们这路想办法优化,首先当两个点不在同一棵树内时我们一定可以连条边,这就是并查集的合并。最后我们连的边一定是连到了一棵树上,我们先固定这棵树,将所有可以连的树全部连到1这个树上(当且仅这个点所在的两片森林中都不在这棵树上时才连)。然后剩下还没连到这棵棵树上的点在两片森林中只有一种情况,即一个连了一个没连。这时候连森林1的和以直接连森林2的点连(画图显而易见),我们用两个set分别记录这些点,然后直接连就行。说的有点模糊,具体见代码,复杂度O(n)。

AC_code:

#include<bits/stdc++.h>
using namespace std;
const int Maxn = 1e6+10;
typedef struct node{
	int u,v;
}Node;
Node ans[Maxn];
int size1[Maxn]={0};
int size2[Maxn]={0};
int fa1[Maxn]={0};
int fa2[Maxn]={0};
int n,m1,m2;
void init(){
	for(int i=1;i<=n;i++){
	fa1[i] = fa2[i] = i;
	size1[i] = size2[i] = 1;
	}
}
/*路径压缩,查找根节点*/
int find1(int x){return x==fa1[x]?x:fa1[x]=find1(fa1[x]);}
int find2(int x){return x==fa2[x]?x:fa2[x]=find2(fa2[x]);}
//合并操作 
void merge1(int x,int y){
	int xx=find1(x), yy=find1(y);
	/*矮数合并到高树上,缩短时间*/
	if(size1[xx]<size1[yy]) swap(xx,yy);
	fa1[yy]=xx;
	size1[xx]+=size1[yy];
	return ;
}
void merge2(int x,int y){
	int xx2=find2(x),yy2 = find2(y);
	if(size2[xx2]<size2[yy2]) swap(xx2,yy2);
	fa2[yy2]=xx2;
	size2[xx2]+=size2[yy2];
	return ;
}
set<int>s1;
set<int>s2;
int main(){
	
	cin>>n>>m1>>m2;
	init();
	for(int i=1;i<=m1;i++){
		int u,v;
		cin>>u>>v;
		merge1(v,u);
	}
	for(int i=1;i<=m2;i++){
		int u,v;
		cin>>u>>v;
		merge2(v,u);
	}
	int sum = 0;
	for(int i=2;i<=n;i++){
		if((find1(1)!=find1(i))&&(find2(1)!=find2(i))){
			ans[++sum].u = 1;
			ans[sum].v = i;
			merge1(1,i);
			merge2(1,i);
		}
	}
	for(int i=2;i<=n;i++){
		if(find1(1)!=find1(i)) s1.insert(find1(i));
		if(find2(1)!=find2(i)) s2.insert(find2(i));
	}
	for(auto a=s1.begin(),b=s2.begin();a!=s1.end()&&b!=s2.end();a++,b++){
		ans[++sum].u = *a;
		ans[sum].v = *b;
	}
	 cout<<sum<<'\n';
	 for(int i=1;i<=sum;i++){
	 	cout<<ans[i].u<<' '<<ans[i].v<<'\n';
	 }
	 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值