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\n",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('\n');
}
return 0;
}
CF1559C
发现有几种情况。
- \(a_1=1\),那么直接从 \(n+1\to 1\to\dots\to n\) 即可。
- \(a_n=0\),那么直接从 \(1\to \dots\to n\to 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\n",ans);
for(auto x:vec) printf("%d %d\n",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\n",ans);
return 0;
}