Codeforces Round #738 (Div. 2) 题解
比赛地址:https://codeforces.com/contest/1559。
补完题了。
A
可以证明,我们能把这个序列变成 \([x,x,\cdots,x]\),其中 \(x=\bigvee_{i=1}^n a_i\)。所以答案就是 \(\bigvee_{i=1}^n a_i\)。
using ll=long long;
void mian(){
int n;scanf("%d",&n);
std::vector<int> a(n);
for(auto &i:a)scanf("%d",&i);
int ans=a[0];
for(auto i:a)ans&=i;
printf("%d\n",ans);
}
B
随便贪心。
using ll=long long;
const int N=100;
int n;
char s[N+10];
void mian(){
scanf("%d%s",&n,s+1);
int fst=-1;
for(int i=1;i<=n;i++)
if(s[i]!='?'){fst=i;break;}
if(fst==-1){
for(int i=1;i<=n;i++){
if(i&1)s[i]='R';
else s[i]='B';
}
puts(s+1);
}
else{
// 22 行的意思是如果 s[i+1] = 'R',则 s[i] = 'B',反之同理。
for(int i=fst-1;i>=1;i--)if(s[i]=='?')s[i]=s[i+1]^'R'^'B';
for(int i=fst+1;i<=n;i++)if(s[i]=='?')s[i]=s[i-1]^'R'^'B';
puts(s+1);
}
}
C
只可能有 3 种方案:
- \(1\to 2\to\cdots\to n\to n+1\)。
- \(n+1\to 1\to 2\to\cdots\to n\)。
- 我们找到一个 \(p\),使得 \(a_p=0\) 且 \(a_{p+1}=1\),那么方案是 \(1\to 2\to\cdots\to p\to n+1\to p+1\to\cdots\to n\)。
using ll=long long;
const int N=1e4;
int n,a[N+10];
void mian(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
if(a[1]==1){
printf("%d ",n+1);
for(int i=1;i<=n;i++)
printf("%d%c",i," \n"[i==n]);
return;
}
if(a[n]==0){
for(int i=1;i<=n+1;i++)
printf("%d%c",i," \n"[i==n+1]);
return;
}
int p=-1;
for(int i=1;i<n;i++)
if(a[i]==0&&a[i+1]==1){p=i;break;}
if(p==-1)puts("-1");
else{
for(int i=1;i<=p;i++)
printf("%d ",i);
printf("%d ",n+1);
for(int i=p+1;i<=n;i++)
printf("%d%c",i," \n"[i==n]);
}
}
D1
我们来证明操作完后一定有一个森林变成树,不妨记那个森林为 \(B\),另外那个为 \(A\)。
考虑 \(A\) 中任意一个点对 \((u,v)\),如果它们在 \(A\) 中不连通,那么它们在 \(B\) 中一定连通,否则就可以把 \((u,v)\) 连上。于是 \(B\) 就变成一棵树了。
贪心。遍历一遍 \((i,j)\)(\(1\le i\lt j\le n\)),能加就加。
using ll=long long;
const int N=1000;
int n,m1,m2;
int ans1[N+10],ans2[N+10];
struct DSU{
int fa[N+10],n;
DSU(int _n=0){init(_n);}
inline void init(int _n){n=_n;for(int i=1;i<=_n;i++)fa[i]=i;}
int getRt(int u){return fa[u]==u?u:fa[u]=getRt(fa[u]);}
inline bool same(int u,int v){return getRt(u)==getRt(v);}
inline void merge(int u,int v){
int fu=getRt(u),fv=getRt(v);
if(fu!=fv)fa[fu]=fv;
}
}d1,d2;
void mian(){
scanf("%d%d%d",&n,&m1,&m2);
d1=DSU(n),d2=DSU(n);
for(int i=1;i<=m1;i++){
int u,v;scanf("%d%d",&u,&v);
d1.merge(u,v);
}
for(int i=1;i<=m2;i++){
int u,v;scanf("%d%d",&u,&v);
d2.merge(u,v);
}
int tot=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++){
if((!d1.same(i,j))&&(!d2.same(i,j))){
ans1[++tot]=i,ans2[tot]=j;
d1.merge(i,j);
d2.merge(i,j);
}
}
printf("%d\n",tot);
for(int i=1;i<=tot;i++)
printf("%d %d\n",ans1[i],ans2[i]);
}
D2
考虑如何优化这个贪心。
先把所有点往 \(1\) 连边,能连就连。然后我们考虑 \(A\) 中不与 \(1\) 连通的点集 \(L\) 和 \(B\) 中不与 \(1\) 连通的点集 \(R\)。那么 \(L\cap R=\varnothing\),且任取 \(l\in L,r\in R\),在图 \(A\) 中 \(l\) 与 \(1\) 不连通,\(r\) 与 \(1\) 连通,\(B\) 中相反。于是任意的 \(l,r\) 都能连边,配个对即可。
using ll=long long;
const int N=1e5;
int n,m1,m2,ans1[N+10],ans2[N+10],a[N+10],b[N+10],cnta,cntb,tota;
struct DSU{
int fa[N+10],n;
DSU(int _n=0){init(_n);}
inline void init(int _n){n=_n;for(int i=1;i<=_n;i++)fa[i]=i;}
int getRt(int u){return fa[u]==u?u:fa[u]=getRt(fa[u]);}
inline bool same(int u,int v){return getRt(u)==getRt(v);}
inline void merge(int u,int v){
int fu=getRt(u),fv=getRt(v);
if(fu!=fv)fa[fu]=fv;
}
}d1,d2;
void mian(){
scanf("%d%d%d",&n,&m1,&m2);
d1.init(n),d2.init(n);
for(int i=1;i<=m1;i++){
int u,v;scanf("%d%d",&u,&v);
d1.merge(u,v);
}
for(int i=1;i<=m2;i++){
int u,v;scanf("%d%d",&u,&v);
d2.merge(u,v);
}
for(int i=2;i<=n;i++)
if((!d1.same(1,i))&&(!d2.same(1,i))){
ans1[++tota]=1;
ans2[tota]=i;
d1.merge(1,i),d2.merge(1,i);
}
for(int i=1;i<=n;i++)
if(!d1.same(1,i))a[++cnta]=i,d1.merge(1,i);
for(int i=1;i<=n;i++)
if(!d2.same(1,i))b[++cntb]=i,d2.merge(1,i);
for(int i=1;i<=std::min(cnta,cntb);i++)
ans1[++tota]=a[i],ans2[tota]=b[i];
printf("%d\n",tota);
for(int i=1;i<=tota;i++)
printf("%d %d\n",ans1[i],ans2[i]);
}
E
先不考虑 \(\gcd\) 的限制。此时这就是一个背包 dp,可以用前缀和优化。这里不赘述了。
设 \(f(a_1,a_2,\cdots,a_n)\) 表示 \(a\) 是否满足前两个条件,现在考虑 \(\gcd\) 的那个条件,答案就是:
根据上式可知每次计算 \(\sum f\) 时只需枚举到 \(\frac md\)。
时间复杂度 \(\mathcal O\left(n\sum_{d=1}^m\frac md\right)=\mathcal O(nm\ln m)\)。
using ll=long long;
const int N=50;
const int M=1e5;
const int P=998244353;
int n,m,l[N+10],r[N+10],f[M+10],sum[M+10];
int prm[M+10],totp,notPrm[M+10],mu[M+10];
void init(){
mu[1]=1;notPrm[1]=1;
for(int i=2;i<=M;i++){
if(!notPrm[i]){prm[++totp]=i;mu[i]=-1;}
for(int j=1;j<=totp&&i*prm[j]<=M;j++){
notPrm[i*prm[j]]=1;
if(i%prm[j]==0){mu[i*prm[j]]=0;break;}
else mu[i*prm[j]]=mu[i]*mu[prm[j]];
}
}
}
int calc(int x){
int M=m/x;
for(int j=0;j<=M;j++)
f[j]=sum[j]=0;
f[0]=1;
for(int j=0;j<=M;j++)
sum[j]=1;
for(int i=1;i<=n;i++){
int L=(l[i]+x-1)/x,R=r[i]/x;
if(L>R)return 0;
for(int j=0;j<=M;j++){
int sumL=j-L>=0?sum[j-L]:0;
int sumR=j-R-1>=0?sum[j-R-1]:0;
f[j]=(sumL-sumR+P)%P;
}
sum[0]=f[0];
for(int j=1;j<=M;j++)
sum[j]=(sum[j-1]+f[j])%P;
}
return sum[M];
}
void mian(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d%d",l+i,r+i);
int ans=0;
for(int i=1;i<=m;i++)
ans+=1LL*mu[i]*calc(i)%P,ans%=P;
printf("%d\n",(ans+P)%P);
}