Codeforces Round #738 (Div. 2) 题解
A
显然的想法是先找出从中选出若干个数使得他们的 \(and\) 和最小,因为设这些位置是 \(p_1,p_2,p_3...p_k\),然后按照 \([p_1,p_2],[p_2,p_3]...[p_{k-1},p_k]\) 的操作顺序把那个数放在 \(p_k\) 上,然后再按照 \([1,p_k],[2,p_k]...[p_k,p_k]...[p_k,n-1],[p_k,n]\) 的顺序把所有数都变成这个数。那么看看所有的数在哪些二进制位上都是 \(1\) 则答案中这一位就是 \(1\) ,否则是 \(0\) 。
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 110
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
int T;
int n,ans;
bool vis[MAXN];
void solve()
{
memset(vis,1,sizeof(vis));
n=read(),ans=0;
FUP(i,1,n)
{
int tmp=read();
FUP(j,0,30)
{
vis[j]&=tmp;
tmp>>=1;
}
}
FDW(i,30,0) ans=(ans<<1)+vis[i];
printf("%d\n",ans);
}
int main(){
T=read();
while(T--) solve();
return 0;
}
B
头上和结尾的 \(?\) 段根据边上的颜色去推,中间交叉着来即可。特判全是 \(?\) 的情况
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 100010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
int T,n;
char s[MAXN];
void solve()
{
n=read();
scanf("%s",s+1);
int p=0;
FUP(i,1,n)
{
p=i;
if(s[i]!='?') break;
}
if(s[p]=='?')
{
FUP(i,1,n)
{
if(i%2) putchar('B');
else putchar('R');
}
puts("");
return;
}
FDW(i,p-1,1) s[i]=s[i+1]=='R'?'B':'R';
p=n+1;
FDW(i,n,1)
{
p=i;
if(s[i]!='?') break;
}
FUP(i,p+1,n) s[i]=s[i-1]=='R'?'B':'R';
FUP(i,1,n) if(s[i]=='?') s[i]=s[i-1]=='R'?'B':'R';
printf("%s\n",s+1);
}
int main(){
T=read();
while(T--) solve();
return 0;
}
C
思路是从头走到尾,中间想办法跑上去。只要找到相邻的两个位置前面是往上走后面是往下走即可。如果没有那么只有三种情况:要么全是往上走,要么全是往下走,要么前面一段是往下走,后面一段是往上走。前两种情况都很容易构造出解,后面的情况我们只需要从往上走的开始点那里往后走,然后到头之后上去,然后下到 \(1\) ,再把剩下的走了。
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 100010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
int T,n,a[MAXN];
bool ck()
{
bool pd=true;
FUP(i,2,n)
{
if(a[i]!=a[i-1])
{
pd=false;
break;
}
}
if(pd)
{
if(a[1]==0)
{
FUP(i,1,n+1) printf("%d ",i);
puts("");
}
else
{
printf("%d ",n+1);
FUP(i,1,n) printf("%d ",i);
puts("");
}
return true;
}
int p1=0,p2=n+1;
FUP(i,1,n)
{
if(a[i]!=1) break;
p1=i;
}
FDW(i,n,1)
{
if(a[i]!=0) break;
p2=i;
}
if(p1+1!=p2) return false;
FUP(i,p2,n) printf("%d ",i);
printf("%d ",n+1);
FUP(i,1,p1) printf("%d ",i);
puts("");
return true;
}
void solve()
{
n=read();
FUP(i,1,n) a[i]=read();
if(ck()) return;
int p=0;
FUP(i,1,n-1)
{
printf("%d ",i);
if(a[i]==0&&a[i+1]==1)
{
p=i+1;
break;
}
}
printf("%d ",n+1);
FUP(i,p,n) printf("%d ",i);
puts("");
}
int main(){
T=read();
while(T--) solve();
return 0;
}
D
其实跟树没有关系,想象成一堆集合即可。设两个森林分别是 \(A,B\) ,考虑先把在两个森林中都不与 \(1\) 在一起的点与 \(1\) 连在一起,然后剩下的就是只在 \(A\) 中与 \(1\) 相邻,以及只在 \(B\) 中与 \(1\) 相邻的点,然后这两种点可以两两匹配,如果可以匹配就合并,注意时时维护集合关系。
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <vector>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 100010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
struct bcj{
int fa[MAXN];
int Find(int u){return fa[u]?fa[u]=Find(fa[u]):u;}
void Union(int u,int v)
{
u=Find(u),v=Find(v);
if(u!=v) fa[u]=v;
}
}A,B;
int n,m1,m2;
vector<pr >re;
vector<int>v1,v2;
int main(){
n=read(),m1=read(),m2=read();
FUP(i,1,m1)
{
int u=read(),v=read();
A.Union(u,v);
}
FUP(i,1,m2)
{
int u=read(),v=read();
B.Union(u,v);
}
FUP(i,1,n)
{
int a=A.Find(1),b=B.Find(1);
int c=A.Find(i),d=B.Find(i);
if(a!=c&&b!=d) re.push_back(mkp(1,i)),A.Union(1,i),B.Union(1,i);
else if(a!=c) v1.push_back(i);
else v2.push_back(i);
}
for(int i=0,j=0;i<(int)(v1.size())&&j<(int)(v2.size());)
{
if(A.Find(1)==A.Find(v1[i]))
{
i++;
continue;
}
if(B.Find(1)==B.Find(v2[j]))
{
j++;
continue;
}
A.Union(v1[i],v2[j]),B.Union(v1[i],v2[j]);
re.push_back(mkp(v1[i],v2[j]));
i++,j++;
}
printf("%d\n",re.size());
for(auto p:re) printf("%d %d\n",p.fi,p.se);
return 0;
}
E
看到 \(\gcd=1\) 就很显然的来一发莫反。设布尔函数 \(g(a_1,a_2...a_n)\) 表示是否满足除了 \(\gcd=1\) 以外的条件,那么显然答案就是
\[\sum_{x_1=l_1}^{r_1}\sum_{x_2=l_2}^{r_2}...\sum_{x_n=l_n}^{r_n}g(x_1,x_2...x_n)[\gcd(x_1,x_2...x_n)=1] \]然后莫反之后就变成了
\[\sum_{d=1}^m\mu(d)\sum_{x_1=\left\lceil\frac{l_1}{d}\right\rceil}^{\left\lfloor\frac{r_1}{d}\right\rfloor}\sum_{x_1=\left\lceil\frac{l_2}{d}\right\rceil}^{\left\lfloor\frac{r_2}{d}\right\rfloor}...\sum_{x_1=\left\lceil\frac{l_n}{d}\right\rceil}^{\left\lfloor\frac{r_n}{d}\right\rfloor}g(x_1d,x_2d...x_nd) \]然后考虑说定义 \(f(l_1,r_1,l_2,r_2...l_n,r_n,M)\) 表示 \(l_i\le a_i\le r_i,\sum_{i=1}^na_i\le M\) 的方案数,这个可以 \(\Theta(nM)\) 的用背包+前缀和优化算,那么后面因为 \(\sum_{i=1}^nx_id\le m\) (根据 \(g\) 的定义),那么 \(\sum_{i=1}^nx_i\le \left\lfloor\dfrac{m}{d}\right\rfloor\) ,所以答案就是
\[\sum_{d=1}^m\mu(d)f(\left\lceil\frac{l_1}{d}\right\rceil,\left\lfloor\dfrac{r_1}{d}\right\rfloor,\left\lceil\frac{l_2}{d}\right\rceil,\left\lfloor\dfrac{r_2}{d}\right\rfloor...\left\lceil\frac{l_n}{d}\right\rceil,\left\lfloor\dfrac{r_n}{d}\right\rfloor,\dfrac{m}{d}) \]然后时间复杂度是 \(\Theta(n\sum_{d=1}^m\dfrac{m}{d})=\Theta(nm\ln m)\) 了。
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 100010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 998244353
#define ll long long
#define db double
using namespace std;
int read()
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
int prm[MAXN],prnum,mu[MAXN];
ll ans;
bool vis[MAXN];
void sieve()
{
mu[1]=1;
FUP(i,2,1e5)
{
if(!vis[i]) prm[++prnum]=i,mu[i]=-1;
FUP(j,1,prnum)
{
if(i*prm[j]>1e5) break;
vis[i*prm[j]]=true;
if(i%prm[j]) mu[i*prm[j]]=mu[i]*mu[prm[j]];
else
{
mu[i*prm[j]]=0;
break;
}
}
}
FUP(i,1,1e5)
{
mu[i]=(mu[i]+MOD)%MOD;
//printf("i=%d mu=%d\n",i,mu[i]);
}
}
int n,m,l[60],r[60];
int dp[60][MAXN],sum[60][MAXN];
int calc(int d)
{
FUP(i,1,n) if((l[i]+d-1)/d>r[i]/d) return 0;
FUP(i,0,m/d) dp[1][i]=sum[1][i]=0;
FUP(j,(l[1]+d-1)/d,r[1]/d) dp[1][j]=1,sum[1][j]=(sum[1][j-1]+1)%MOD;
FUP(j,r[1]/d+1,m/d) sum[1][j]=sum[1][j-1];
FUP(i,2,n)
{
if((l[i]+d-1)/d>r[i]/d) return 0;
FUP(j,(l[i]+d-1)/d,m/d)
{
dp[i][j]=sum[i-1][j-(l[i]+d-1)/d];
if(j>=r[i]/d) dp[i][j]=(dp[i][j]-sum[i-1][j-r[i]/d-1]+MOD)%MOD;
sum[i][j]=(sum[i][j-1]+dp[i][j])%MOD;
//printf("i=%d j=%d dp=%d sum=%d\n",i,j,dp[i][j],sum[i][j]);
}
}
return sum[n][m/d];
}
int main(){
sieve();
n=read(),m=read();
FUP(i,1,n) l[i]=read(),r[i]=read();
FUP(i,1,m)
{
ans=(ans+(ll)(mu[i])*calc(i)%MOD)%MOD;
}
printf("%lld\n",ans);
return 0;
}