Codeforces Round #836 (Div. 2)

文章介绍了几个来自Codeforces编程竞赛的题目,涉及字符串的双倍回文构造、异或值等于平均数的数列构建、特定条件的数字排列以及求解特定范围内的平方和问题。每道题目都给出了C++的解决方案,并重点解析了问题的关键分析和算法思路。

A. SSeeeeiinngg DDoouubbllee

 

题目链接:Problem - A - Codeforces

样例输入: 

4
a
sururu
errorgorn
anutforajaroftuna

样例输出:

aa
suurruurruus
rgnororerrerorongr
aannuuttffoorraajjaarrooffttuunnaa

题意:假如一个字符串是abc,那么他的double串就是aabbcc,现在给定一个字符串,我们可以对这个字符串的double串进行随意排列,输出一种回文串的排列。

分析:一种最简单的方式就是把原字符串正着输出一遍然后再倒着输出一遍即可。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=103;
char s[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		scanf("%s",s+1);
		for(int i=1;i<=strlen(s+1);i++)
			printf("%c",s[i]);
		for(int i=strlen(s+1);i>=1;i--)
			printf("%c",s[i]);
		printf("\n");
	}
	return 0;
} 

B. XOR = Average

题目链接:Problem - B - Codeforces

样例输入: 

3
1
4
3

样例输出:

69
13 2 8 1
7 7 7

题意:给定一个n,让我们构造n个数,使得这n个数的异或值等于这n个数的平均数。

分析:当n为奇数时我们直接令n个数相同,这样就使得异或值等于自己,平均值也等于自己

当n为偶数时我们可以令前两个数等于6和2,其余数全部等于4即可。其实就是找到三个数x,y,z满足x异或y=(x+y)/2=z

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		if(n&1)
		{
			for(int i=1;i<=n;i++)
				printf("1 ");
		}
		else
		{
			printf("2 6 ");
			for(int i=3;i<=n;i++)
				printf("4 ");
		}
		puts("");
	}
	return 0;
}

C. Almost All Multiples

题目链接:Problem - C - Codeforces

样例输入: 

3
3 3
4 2
5 4

样例输出:

3 2 1 
2 4 3 1 
-1

题意:给定一个n和x,让我们构造一个1~n的排列,满足p1=x,pn=1,且对于所有的2<=i<=n-1,都有pi%i==0.要求输出最小的字典序

分析:先不考虑字典序最小的问题,假如n%x等于0,那么我们可以令px=n,这样的话我们就确定了3个位置,其余位置等于自身即可,这样我们一定是可以构造出来的。假如n%x!=0,那么第x个位置我们只能填k*x,那么对应的第k*x个位置我们还要填一个p*k*x,如果最后一个关于x的倍数位置不能填n,那么就无法构成这样的排列,所以对于n%x!=0的情况是无法构造出来的。现在来考虑一下字典序的问题,对于n%x==0,那么对于第x个位置我们先考虑用2*x来填写,我们要保证填写的这个数一定要是n的因子而且是未被填写过,否则到这个位置上的时候也无法用n替代那么也是不行的。如果2倍不行就3倍,依次递增,这样一定能够构造出满足题意的最小字典序。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=2e5+10;
int a[N];
bool vis[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n,x;
		scanf("%d%d",&n,&x);
		if(n%x==0)
		{
			for(int i=1;i<=n;i++) vis[i]=false;
			a[1]=x;a[n]=1;
			vis[x]=vis[1]=true;
			printf("%d ",x);
			for(int i=2;i<n;i++)
			{
				if(!vis[i])
				{
					printf("%d ",i);
					vis[i]=true;
					continue;
				}
				for(int j=2;j*i<=n;j++)
				{
					if(!vis[j*i]&&n%(j*i)==0)
					{
						vis[j*i]=true;
						printf("%d ",j*i);
						break;
					}
				}
			}
			puts("1");
		}
		else
			puts("-1");
	}
	return 0;
}

D. Range = √Sum

题目链接:Problem - D - Codeforces

样例输入: 

3
2
5
4

样例输出:

3 1
20 29 18 26 28
25 21 23 31

题意:给定一个n,让我们构造n个互不相同的数使得满足

分析:当n为偶数时,我们可以直接按照n-1,n+1,n-2,n+2……,n-n/2,n+n/2构造

这样n个数中的最大值减最小值就等于n,而且n个数的和就是n*n,刚好满足题意。

如果当n是奇数时我们依旧可以在偶数构造的方式上做出改变,先将n自增变为偶数,然后去掉n-1这个数,这个时候就剩了奇数个数,然后将其余n-1个数都加上1,这样就可以保证极差不变而且总和也不变,也是满足题意的。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		if(n&1)
		{
			n++;
			printf("%d ",n+2);
			for(int i=2;i<=n/2;i++)
				printf("%d %d ",n+i+1,n-i+1);
		}
		else
		{
			for(int i=1;i<=n/2;i++)
				printf("%d %d ",n+i,n-i);
		}
		puts("");
	}
	return 0;
}

E. Tick, Tock

题目链接:Problem - E - Codeforces

样例输入:

5
2 3 4
1 0 -1
-1 -1 2
2 2 10
1 2
3 5
4 5 1024
1 -1 -1 -1 -1
-1 -1 -1 1000 -1
-1 -1 -1 -1 69
420 -1 -1 999 -1
3 3 3
-1 -1 1
2 -1 1
2 -1 2
3 3 3
1 -1 2
-1 0 -1
-1 1 0

样例输出:

4
0
73741817
0
1

题意:给定一个n*m的矩阵和一个h,每个矩阵中有一个数,矩阵中每个单元有一个数,这个数为-1或者是0~h-1中的一个数,如果说这个单元中的数是-1,那么就说明这个数可以随意指派为0~h-1中的一个数,我们可以对这个矩阵进行操作,每次操作选定一行或者一列将其所有的数在模h运算下加1,问有多少种初始方案可以使得在对矩阵操作完后矩阵中的所有数都相同。答案对1e9+7取余。

分析:我们可以发现对一行或者一列的操作次数一定是在0~h-1之间的,因为操作次数等于h次时相当于没有操作,所以操作次数一定小于h,而且一个数(i,j)的最终值取决于三个数,一个是对第i行的操作次数r[i],另一个是对第j列的操作次数c[j],最后一个是这个数的初始值a[i][j].最终值就是(a[i][j]+r[i]+c[j])%h.假如我们可以把整个矩阵都变为值1,那么我们就可以把矩阵都变为任意小于h的非负数,因为我们变为一个值后可以只对列或者行操作从而改变整个矩阵的值。所以最终变成的值并不重要。不妨假设最终变成的值是0.我们可以把每一行浓缩成一个点,那么一共有n个点,再把每一列浓缩成一个点,有m个点,为了与前n个点区分开,我们让后m个点的标号为n+1~n+m。这样对于每一个a[i][j]!=-1的位置,我们都可以从i号点到(n+j)号点之间连一条权值为a[i][j]的双向边,这样我们建完图后图中一定要满足任意两个点之间如果有边,那么一定要满足两点点权加上边权之和对h取余得0的限制条件,我们对建成后的图进行一遍dfs染色,对于一个连通块,只要连通块中某一个点的值确定了那么这个连通块中的所有数就都确定了,,所以我们只需要搜索一遍连通块如果没有发生矛盾.那么就说明可以将该连通块上的点变成一个值。但是整张图并不一定是一个连通块,我们最终的目的是要把整张图构造成一个连通块,之所以整张图不一定连通就是因为有一些边a[i][j]=-1,而对于这种边我们在建图时是没有加入的。假如原图一共有cnt个连通块,那么就需要cnt-1条边来使得整张图连通,边的含义我们已经很清楚了,就是代表a[i][j]的取值,取值集合是0~h-1一共h种情况,由于每个连通块都可以独立地变为任意值,那么两个连通块之间的边权就可以变为任意值,只要保证点权和+边权对h取余等于0即可,所以边权有h种取值,总共有cnt-1条连边,所以答案就是h^(cnt-1)。但是如果出现矛盾,那么答案就是0.

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e6+10,mod=1e9+7;
int e[N],ne[N],w[N],idx,h[N];
int a[N],n,m,hh;
long long p[N];
bool vis[N];
int find(int x,int y)
{
	return (x-1)*m+y;
}
void add(int x,int y,int z)
{
	e[idx]=y;
	w[idx]=z;
	ne[idx]=h[x];
	h[x]=idx++;
}
bool dfs(int x)
{
	vis[x]=true;
	bool ans=true;
	for(int i=h[x];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(vis[j])
		{
			if((p[x]+p[j]+w[i])%hh!=0)//出现矛盾 
			{
				ans=false;
				break;
			}
			continue;
		}
		p[j]=(hh*2-w[i]-p[x])%hh;
		ans&=dfs(j);
	}
	return ans;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		scanf("%d%d%d",&n,&m,&hh);
		idx=0;
		for(int i=1;i<=n+m;i++)
		{
			h[i]=-1;
			vis[i]=false;
		}
		for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&a[find(i,j)]);
			if(a[find(i,j)]!=-1)
			{
				add(i,n+j,a[find(i,j)]);
				add(n+j,i,a[find(i,j)]);
			}
		}
		long long ans=1;
		int cnt=0;
		for(int i=1;i<=n+m;i++)
			if(!vis[i])
			{
				if(dfs(i)) cnt++;
				else
				{
					ans=0;
					break;
				}
			}
		for(int i=1;i<cnt;i++)
			ans=ans*hh%mod;
		printf("%lld\n",ans);
	}
	return 0; 
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值