CF1559D 题解

https://codeforces.com/contest/1559

CF1559A

把题目中的操作范围缩小一下,可以变成相邻两个按位与一下。我们不断地操作,必然能把所有数都变成所有数地按位与。

#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=109;

inline long long read() {
	long long res=0, w=1; char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) {res=res*10+c-48; c=getchar();}
	return res*w;
}

int T,n,a[N];

signed main() {
	T=read();
	while(T--) {
		n=read(); int ans=(1ll<<30)-1;
		rep(i,1,n) a[i]=read(), ans&=a[i];
		printf("%lld
",ans);
	}
	return 0;
}

CF1559B

考虑对于一段连续地问号区间:如果在一开始,那么就从后往前贪心;如果在最后面,就从前往后贪心;否则从两边贪心即可(其实现在想了想没这个必要,从一端贪心应该也是对的)。

#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=109;

inline long long read() {
	long long res=0, w=1; char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) {res=res*10+c-48; c=getchar();}
	return res*w;
}

int T,n;
char s[N];

signed main() {
	T=read();
	while(T--) {
		scanf("%lld%s",&n,s+1);
		int l=0;
		rep(i,1,n) {
			if(s[i]=='?'&&s[i-1]!='?') l=i;
			if(s[i]=='?'&&s[i+1]!='?') {
				int r=i;
				if(l==1) {
					per(i,r,l) {
						if(s[i+1]=='B') s[i]='R';
						else s[i]='B';
					}
				} else if(r==n) {
					rep(i,l,r) {
						if(s[i-1]=='B') s[i]='R';
						else s[i]='B';
					}
				} else {
					while(l<=r) {
						if(s[l-1]=='B') s[l++]='R';
						else s[l++]='B';
						if(s[r+1]=='B') s[r--]='R';
						else s[r--]='B';
					}
				}
			}
		}
		rep(i,1,n) putchar(s[i]); putchar('
');
	}
	return 0;
}

CF1559C

发现有几种情况。

  • (a_1=1),那么直接从 (n+1 o 1 odots o n) 即可。
  • (a_n=0),那么直接从 (1 o dots o n o n+1) 即可。
  • 如果上面两种都不成立,必然存在相邻的两个使得 (a_i=0,a_{i+1}=1),直接在这两个中间插入一个 (n+1) 即可。
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9;

inline long long read() {
	long long res=0, w=1; char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) {res=res*10+c-48; c=getchar();}
	return res*w;
}

int T,n,a[N];

signed main() {
	T=read();
	while(T--) {
		n=read(); 
		rep(i,1,n) a[i]=read();
		if(a[1]==1) {
			printf("%lld ",n+1);
			rep(i,1,n) printf("%lld ",i);
			puts("");
		} else if(a[n]==0) {
			rep(i,1,n) printf("%lld ",i);
			printf("%lld ",n+1);
			puts("");
		} else {
			int pos=0;
			rep(i,1,n-1) if(a[i]==0&&a[i+1]==1) {pos=i; break;}
			rep(i,1,pos) printf("%lld ",i);
			printf("%lld ",n+1);
			rep(i,pos+1,n) printf("%lld ",i);
			puts("");
		}
	}
	return 0;
}

CF1559D

两个放一起写吧。首先容易感性地理解发现,由于每次两边都减少一个连通块,并且都是减少 (u,v) 所在连通块,所以我们只要暴力枚举哪些点没联通然后直接连上就行了。这样就可以过 D1。

D2 就比较有意思了。假设我们钦定一个中心点 (s)。那么首先两个森林中对于 (u) 如果 (s) 都和 (u) 不连通,那么必然能连。连完后还有些不连通的,我们发现对于点 (u,v),它们必然不会在两棵树上都与 (s) 联通。如果 (u) 在第一棵树上与 (s) 联通并且 (v) 在第二棵树上与 (s) 联通,那么我们必然能连接 ((u,v))(如果它们在任意一棵树上已经联通,那么必然导致存在一个点与 (s) 都联通)。然后我们找到这些点,匹配一下即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9;
typedef pair<int,int> pii;

inline long long read() {
	long long res=0, w=1; char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) {res=res*10+c-48; c=getchar();}
	return res*w;
}

int n,m1,m2,id1[N],id2[N],c1[N],c2[N],ans;
void init(int *id) {rep(i,1,n) id[i]=i;}
int find(int *id,int i) {return i==id[i]?i:id[i]=find(id,id[i]);}
void unite(int *id,int u,int v) {id[find(id,u)]=find(id,v);}
vector<pii>vec;
vector<int>ar1,ar2;

int main() {
	n=read(), m1=read(), m2=read();
	init(id1), init(id2);
	rep(i,1,m1) {
		int u=read(), v=read();
		unite(id1,u,v);
	}
	rep(i,1,m2) {
		int u=read(), v=read();
		unite(id2,u,v);
	}
	int s=rand()%n+1;
	rep(i,1,n) {
		c1[i]=(find(id1,i)==find(id1,s));
		c2[i]=(find(id2,i)==find(id2,s));
	}
	rep(i,1,n) if((find(id1,i)==find(id1,s))+(find(id2,i)==find(id2,s))==0)
		vec.push_back(pii(s,i)), unite(id1,s,i), unite(id2,s,i);
	rep(i,1,n) {
		if(find(id1,i)!=find(id1,s)) ar1.push_back(i), unite(id1,s,i);
		if(find(id2,i)!=find(id2,s)) ar2.push_back(i), unite(id2,s,i);
	}
	int sz=min(ar1.size(),ar2.size());
	rep(i,0,sz-1) {
		vec.push_back(pii(ar1[i],ar2[i]));
	}
	ans=vec.size();
	printf("%d
",ans);
	for(auto x:vec) printf("%d %d
",x.first,x.second);
	return 0;
}

CF1559E

小清新容斥 DP 题。考虑普通背包计数是 (O(nm)) 的。然后再看 GCD 的限制。对于这种东西,我们可以非常自然地想到容斥这个 GCD (d),容斥系数容易发现就是莫比乌斯函数。一个小优化,若有 (mu(d)=0) 那么我们可以直接跳过,可以优化一些常数。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9,mod=998244353;

inline long long read() {
	long long res=0, w=1; char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) {res=res*10+c-48; c=getchar();}
	return res*w;
}

int n,m,pl[N],prr[N],l[N],r[N],f[N],s[N],pr[N],cnt,mu[N],vst[N],ans;

void sieve(int n){
	mu[1]=1;
	for(int i=2;i<=n;i++){
		if(!vst[i]) pr[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&i*pr[j]<=n;j++){
			vst[i*pr[j]]=1;
			if(i%pr[j]==0){mu[i*pr[j]]=0;break;}
            mu[i*pr[j]]=-mu[i];
		}
	}
}

signed main() {
	n=read(), m=read();
	sieve(m);
	rep(i,1,n) pl[i]=read(), prr[i]=read();
	rep(d,1,m) if(mu[d]) {
		rep(i,1,n) l[i]=(pl[i]+d-1)/d, r[i]=prr[i]/d;
		int nm=m/d;
		rep(j,0,nm) s[j]=1;
		rep(i,1,n) {
			rep(j,1,nm) f[j]=0;
			rep(j,l[i],nm) {
				f[j]=s[j-l[i]];
				if(j-r[i]-1>=0) f[j]=(f[j]+mod-s[j-r[i]-1])%mod;
			}
			s[0]=0;
			rep(j,1,nm) s[j]=(s[j-1]+f[j])%mod;
		}
		ans=(ans+mu[d]*s[nm])%mod;
	}
	ans=(ans+mod)%mod;
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/TetrisCandy/p/15146216.html