Codeforces Round #738 (Div. 2) 题解

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;
}
上一篇:2110. 股票平滑下跌阶段的数目 组合数


下一篇:BUUCTF:[SWPU2019]Network