Codeforces Round #738 (Div. 2)

Codeforces Round #738 (Div. 2)


A. Mocha and Math

题目大意:

​ 有一个数列,可以选择无限次区间,把区间[l,r]里的第i个数替换成 a[l+i] & a[r-i]

要求这个序列最后可能的最大值里的最小值。

思路:

​ 有关位运算的题目就用二进制来看,有n个数,就把他当成是一个n*k的二维数组,其中k是最大数的二进制位数,本题k取30就可以(a[i]<=1e9)。这种想法在二进制位运算里很常见,可以康一康这个Moamen and XOR

​ 因为可以执行无数次题中所描述的操作,而我们要找n个数里最大值的最小值,所以对于每一个二进制位,可以变0就尽量变0 。那只要n个数里有一个二进制位i是0,就让它变0就可以了。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
int n,m,T;

inline ll read(){
    ll ans = 0;
    char c = getchar();
    while (!isdigit(c))
        c = getchar();
    while (isdigit(c)){
        ans = ans * 10 + c - '0';
        c = getchar();
    }return ans;
}

int main(){
	T = read();
	while(T--){
		n = read();
		bitset<30> a[200];//bitset是位运算里很常用的
		ll b = 1,ans = 0;
		for(int i = 1;i <= n; i++){
			ll t = read();
			a[i] = (t);
		}

		for(int j = 0;j <= 30; j++){
			int f = 1;
			for(int i = 1;i <= n; i++)
			if(a[i][j] != a[1][j]){
				f = 0;break;
			}
			if(f && a[1][j])ans += b;
			b <<= 1;
		}
		
		printf("%lld\n",ans);
	}
}

写完以上代码a掉后我一想,好像我在做的事情就是把n个数字与起来啊,淦!然后我又打了一个直接全部与起来的,a掉了。不知道应该是什么心情,人麻了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 2*1e8+10;

inline int read(){
    int ans = 0;
    char c = getchar();
    while (!isdigit(c))
        c = getchar();
    while (isdigit(c)){
        ans = ans * 10 + c - '0';
        c = getchar();
    }return ans;
}

int T,n,tmp,ans; 
int main(){
	T = read();
	while(T--){
		n = read();
		ans = read();
		for(int i = 2;i <= n; i++){
			tmp = read();
			ans &= tmp;
		}
		printf("%d\n",ans);
	}
}

===================================================================

B.Mocha and Red and Blue

题目大意:

​ 有蓝红两种颜色,如果相邻两格是一样的颜色就要加分数,请你再?处涂色,使分数最小。

思路:

​ 分两种情况考虑:

​ 1.开头?,找后面第一个颜色,交替往前涂色。

​ 2.中间?,找前面第一个颜色,交替往后涂色。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 2*1e8+10;

int T;
ll n,m,ans;

inline ll read(){
    ll ans = 0;
    char c = getchar();
    while (!isdigit(c))
        c = getchar();
    while (isdigit(c)){
        ans = ans * 10 + c - '0';
        c = getchar();
    }return ans;
}

ll find(string s,char x,ll st){
	for(ll i = st;i < s.length(); i++)
	if(s[i] == x)return i;
	return n;
}

int main(){
	T = read();
	while(T--){
		n = read();
		string s;
		s.clear();
		cin>>s;
		ll poschr=1,pos = find(s,'?',0);
		while(1){
			if(pos == -1)break;
			ll B = find(s,'B',pos);
			ll R = find(s,'R',pos);
			poschr = (B < R)?B:R;
			if(poschr == n)break;
			if(!pos){
				for(int i = poschr-1;i >= 0; i--){
					s[i] = (s[i+1] == 'B')?'R':'B';
				}
			}else{
				for(int i = pos;i < poschr ; i++){
					s[i] = (s[i-1] == 'B')?'R':'B';
				}
			}
			pos = find(s,'?',poschr+1);
			if(pos == n)break;
		}
		if(poschr == n){//末尾?单独考虑
			for(int i = find(s,'?',0);i <= n; i++){
				s[i] = (s[i - 1] == 'B')?'R':'B';
			}
		}
		cout<<s<<"\n";
	}
}

害,这么简单的题wa了好几次,所以说小数据别想得太多,直接暴力就好。

===================================================================

C.Mocha and Hiking

题目大意:

​ 有n+1个点,前n-1个点都和后一个点相通(1到2,2到3,...,n-2到n-1),然后给n个0或1,0代表i连n+1,1代表n+1连i。问能不能每个点不重复地遍历到一遍。

思路:

首先,分析一下是不会有-1的情况的。全0,全1,首0尾1中间1,首0尾1中间0,都是可以走得通的。所以走通分成了一下两种情况:

case1:

若n+1直接能走到1,不论后面怎么样直接n+1,1,2,3,4,……;

若n能直接走到n+1,直接1,2,3,……,n,n+1。如下图:

case2:

n+1在中间,要通过某个数x到n+1,再从n+1到x+1 。这种情况体现在输入数据上就是有“01”。如下图:

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e4+50;

int T,n;
int a[N];

inline ll read(){
    ll ans = 0;
    char c = getchar();
    while (!isdigit(c))
        c = getchar();
    while (isdigit(c)){
        ans = ans * 10 + c - '0';
        c = getchar();
    }return ans;
}

int main(){
	scanf("%d",&T);
	while(T -- ){
		scanf("%d",&n);
		memset(a,0,sizeof a);
		for(int i = 1;i <= n; i++)
		scanf("%d",&a[i]);
		if(!a[n]){
			for(int i = 1;i <= n+1; i++)
			printf("%d ",i);printf("\n");
			continue;
		}
		if(a[1]){
			printf("%d ",n+1);
			for(int i = 1;i <= n; i++)
			printf("%d ",i);printf("\n");
			continue;
		}
		int f = 1;
		for(int i = 1;i < n; i++){
			printf("%d ",i);
			if(!a[i] && a[i+1] && f){
				printf("%d ",n+1);
				f = 0;
			}
		}printf("%d\n",n);
	}
}

D1.Mocha and Diana (Easy Version)

D2.Mocha and Diana (Hard Version)

这两题本质上是一样的,只是数据范围的区别。用o(n)的方法都能解决。

题目大意:

有两个森林,若在两个森林里的对应两点都不在同一颗树上,那么在可以在他们之间加一条边。问最多可以加多少条边。

思路:

将所有在两个森林里的点都和1连起来。这样之后森林1里不和点1连通的就必在森林2里和点1连通 。同理,森林2里不和点1连通的就必在森林1里和点1连通。(这里可以想一想)

最后只要找到两个森林里的“孤儿”连起来就行了。

判断是否相连用一下并查集就行,注意要路径优化。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e5+500;
int n,m1,m2,u,v;
int sup[3][N];
vector<pair<int,int> >ans;

inline ll read(){
    ll ans = 0;
    char c = getchar();
    while (!isdigit(c))
        c = getchar();
    while (isdigit(c)){
        ans = ans * 10 + c - '0';
        c = getchar();
    }return ans;
}

int find(int x,int num){
	return sup[num][x] == x? x : sup[num][x] = find(sup[num][x],num);
}

void join(int x,int y,int num){
	int supx = find(x,num);
	int supy = find(y,num);
	if(supx < supy)sup[num][supy] = supx;
	if(supx > supy)sup[num][supx] = supy;
}

int main(){
	n = read();
	m1 = read();
	m2 = read();
	for(int i = 1;i <= n; i++)
	sup[1][i] = sup[2][i] = i;
	
	for(int i = 1;i <= m1; i++){
		u = read();
		v = read();
		join(u,v,1);
	}
	for(int i = 1;i <= m2; i++){
		u = read();
		v = read();
		join(u,v,2);
	}
	
	for(int i = 2;i <= n; i++){
		if(find(i,1) != 1 && find(i,2) != 1){
			join(1,i,1);
			join(1,i,2);
			ans.push_back(make_pair(1,i));
		}
	}

	for(int i = 2,j = 2 ;i <= n; i++){
		if(find(i,1) != 1){
			while(j <= n && find(j,2) == 1)j++;
			if(j > n)break;
			join(i,1,1);
			join(j,1,2);
			ans.push_back(make_pair(i,j));
		}
	}
	printf("%d\n",ans.size());
	for(int i = 0;i < ans.size(); i++)
	printf("%d %d\n",ans[i].first,ans[i].second);
	
}

===================================================================

写不动了最后一题下次再更 886

posted @ 2021-08-17 21:33  tyrii  阅读(20)  评论(0)    收藏  举报