Codeforces Round #659 Div. 2 题解

A

随便构造一下就好了。


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100010
 
int T,n,a[maxn];
char s[maxn];
 
int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);int ma=1;
		for(int i=1;i<=n;i++)scanf("%d",&a[i]),ma=max(a[i],ma);
		for(int i=1;i<=ma;i++)s[i]='a',printf("%c",s[i]);printf("\n");
		for(int i=1;i<=n;i++)
		{
    
    
			for(int j=a[i]+1;j<=ma;j++)if(s[j]=='a')s[j]='b';else s[j]='a';
			for(int j=1;j<=ma;j++)printf("%c",s[j]);printf("\n");
		}
	}
}

B1&B2

对于每个能够永久待下去的位置,肯定选择将时间调整到潮水最高的时候,然后再继续往下走,因为此时潮水是在下降的,更利于通过后面的位置。

对于不能永久呆下去的位置,即潮水到了某个高度后就会GG,那么肯定越快走越好,求出下一个位置最早什么时候能走,然后尽早走过去即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 300010
 
int T,n,k,l;
 
int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d %d %d",&n,&k,&l);
		int now=k,dir=-1,ans=1;//now是现在潮水水位,dir表示潮水在下降还是上升
		for(int i=1;i<=n;i++)
		{
    
    
			int x;scanf("%d",&x);
			if(x>l)ans=0;else x=l-x;
			if(!ans)continue;
			if(x>=k)now=k,dir=-1;
			else if(dir==-1){
    
    
				if(now>x)now=x;
				else now--;
				if(now==0)dir=1;
			}else{
    
    
				if(now<x)now++;
				else ans=0;
			}
		}
		if(ans)printf("Yes\n");
		else printf("No\n");
	}
}

C

容易将问题转化为图论问题,如果 A i ≠ B i A_i\neq B_i Ai=Bi,那么就将这两个字符连一条有向边。

对于一个连通块,从小到大依次转化肯定是最优的解,比如对于这样一个连通块: a → b , b → d , a → c , c → d a\to b,b\to d,a\to c,c\to d ab,bd,ac,cd,那么转化顺序为 a → b → c → d a\to b\to c\to d abcd

所以需要的次数就是连通块大小 − 1 -1 1,总的答案就是字符集 − - 连通块数量。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100010
 
int T,n;
char s1[maxn],s2[maxn];
int fa[maxn],occ[maxn];
int findfa(int x){
    
    return x==fa[x]?x:fa[x]=findfa(fa[x]);}
void link(int x,int y){
    
    
	x=findfa(x);y=findfa(y);
	if(x!=y)fa[y]=x;
}
 
int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);
		scanf("%s %s",s1+1,s2+1);
		int ans=0;
		for(int i=1;i<=20;i++)fa[i]=i,occ[i]=0;
		for(int i=1;i<=n;i++)
		{
    
    
			if(s1[i]>s2[i]){
    
    ans=-1;break;}
			else link(s1[i]-'a'+1,s2[i]-'a'+1);
			occ[s1[i]-'a'+1]=occ[s2[i]-'a'+1]=1;
		}
		if(ans!=-1)for(int i=1;i<=20;i++)if(occ[i])ans+=occ[i]-(i==fa[i]);
		printf("%d\n",ans);
	}
}

D

既然是异或,那么考虑两人最后 最高的不相同的位即可。

统计一下每一位有多少个 1 1 1,从高位往低位看,记这一位有 x x x 1 1 1 y y y 0 0 0

假如 x ≡ 0 ( m o d 2 ) x\equiv 0\pmod 2 x0(mod2),那么最后两人在这一位的值一定是相同的,跳过不管。

扫描二维码关注公众号,回复: 11762436 查看本文章

假如 x ≡ 1 ( m o d 4 ) x\equiv 1\pmod 4 x1(mod4),那么先手拿一个 1 1 1 之后, 1 1 1 的数量就是 4 4 4 的倍数了,可以发现,无论 y y y 是奇数还是偶数,最后这 x − 1 x-1 x1 1 1 1 一定会被两人平分,由于 x − 1 2 \dfrac {x-1} 2 2x1 是偶数,所以不产生贡献,那么最后先手的这一位就是 1 1 1,后手的这一位就是 0 0 0,先手必胜。

假如 x ≡ 3 ( m o d 4 ) x\equiv 3\pmod 4 x3(mod4),如果两人轮流拿 1 1 1,那么先手必败。若 y ≡ 1 ( m o d 2 ) y\equiv 1\pmod 2 y1(mod2),那么先手只需要设法拿到最后一个 0 0 0,那么后手就变成了此时的先手,后手就必败了。

总结一下,只有当 x ≡ 3 ( m o d 4 ) x\equiv 3\pmod 4 x3(mod4) y ≡ 0 ( m o d 2 ) y\equiv 0\pmod 2 y0(mod2) 时,后手才能赢。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
 
int T,n,b[40];
 
int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);
		memset(b,0,sizeof(b));
		for(int i=1,x;i<=n;i++){
    
    
			scanf("%d",&x);
			for(int j=0;j<31;j++)if(x>>j&1)b[j]++;
		}
		int ans=-1;
		for(int j=30;j>=0;j--)if(b[j]%2){
    
    
			if(b[j]%4==3&&(n-b[j])%2==0)ans=0;
			else ans=1;
			break;
		}
		if(ans==1)printf("WIN\n");
		if(ans==0)printf("LOSE\n");
		if(ans==-1)printf("DRAW\n");
	}
}

E

C C C 题不同的是,这题的图中可能存在环,假如有环,那么解中也一定存在环。

容易发现,最优解一定不会经过一个点两次以上,对于一个连通块,设解中包含 k k k 条边,考虑用如下方法构造一个DAG:

  1. 依次考虑每条边 ( x , y ) (x,y) (x,y)
  2. 假如 x , y x,y x,y 在同一个DAG中,那么删去点 y y y
  3. 否则连接 x , y x,y x,y 这条边

可以发现,每一个简单环都会使得图中少一个点。设原图点数为 n n n,那么最后构造出来的DAG一定有 n − 1 n-1 n1 条边,那么有 k − n + 1 k-n+1 kn+1 条边会使得图中的点被删掉,那么最后图的大小就是 ∣ D A G ∣ = n − ( k − n + 1 ) = 2 n − k − 1 |DAG|=n-(k-n+1)=2n-k-1 DAG=n(kn+1)=2nk1

移项得到 k = 2 n − ∣ D A G ∣ − 1 k=2n-|DAG|-1 k=2nDAG1,要使得 k k k 最小,那么就要让 ∣ D A G ∣ |DAG| DAG 最大,那么 d p dp dp 一下找到图中最大的 D A G DAG DAG 即可。

注意, 2 n − k − 1 2n-k-1 2nk1 是一个连通块的答案,总的答案应该是 2 N − ∣ D A G ∣ m a x − 2N-|DAG|_{max}- 2NDAGmax连通块数。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100010
#define maxm 20

int T,n;
char s1[maxn],s2[maxn];
int reach[maxm],cnt,fa[maxm];
int findfa(int x){
    
    return x==fa[x]?x:fa[x]=findfa(fa[x]);}
void link(int x,int y){
    
    
	x=findfa(x);y=findfa(y);
	if(x!=y)fa[y]=x;
}
bool dag[1<<maxm];
int c[1<<maxm],ans;

int main()
{
    
    
	for(int i=1;i<(1<<20);i++)c[i]=c[i-(i&-i)]+1;
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);
		scanf("%s %s",s1+1,s2+1);
		memset(reach,0,sizeof(reach));cnt=0;
		memset(dag,false,sizeof(dag));
		for(int i=0;i<20;i++)fa[i]=i;
		for(int i=1;i<=n;i++)link(s1[i]-'a',s2[i]-'a'),reach[s1[i]-'a']|=(1<<(s2[i]-'a'));
		for(int i=0;i<20;i++)if(i==fa[i])cnt++;
		dag[0]=true;c[0]=ans=0;
		for(int i=0;i<(1<<20);i++){
    
    
			if(dag[i])ans=max(ans,c[i]);
			for(int j=0;j<20;j++)
			if(dag[i]&&(reach[j]&i)==0)dag[i|(1<<j)]=true;
		}
		printf("%d\n",40-ans-cnt);
	}
}

F

直接看构造方法吧,知道方法之后正确性比较显然,因为后面放的数都没有前面放的数大,所以不需要考虑答案会不合法。

代码如下:

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 300

int n,m,a[maxn][maxn],ans[maxn][maxn];
int line[maxn*maxn],col[maxn*maxn];
struct par{
    
    int x,y;}q[maxn*maxn];
int st=0,ed=0;

int main()
{
    
    
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++){
    
    
		int ma=0;
		for(int j=1;j<=m;j++)ma=max(ma,a[i][j]);
		line[ma]=1;
	}
	for(int j=1;j<=m;j++){
    
    
		int ma=0;
		for(int i=1;i<=n;i++)ma=max(ma,a[i][j]);
		col[ma]=1;
	}
	int X=0,Y=0;
	for(int i=n*m;i>=1;i--)
	{
    
    
		X+=line[i],Y+=col[i];
		if(line[i]||col[i])ans[X][Y]=i;
		else ans[q[st].x][q[st].y]=i,st++;
		if(line[i])for(int j=Y-1;j>=1;j--)q[ed++]=(par){
    
    X,j};
		if(col[i])for(int j=X-1;j>=1;j--)q[ed++]=(par){
    
    j,Y};
	}
	for(int i=1;i<=n;i++){
    
    
		for(int j=1;j<=m;j++)printf("%d ",ans[i][j]);
		printf("\n");
	}
}

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/107877092
今日推荐