Codeforces Round 903 (Div. 3)补题

Don't Try to Count

题目大意:给定一个n长的字串x,一个m长的字串s,我们可以对x进行x+=x,问至少操作多少次后s是x的子串,如果不能实现输出-1.

思路:这里查找是否是子串直接用find即可,问题的关键在于x+=x最多执行几次,实际上我们可以发现x叠加一次会出现新的字符串,再叠加就未必还会出现新的子串了,所以首先,x叠加一次,如果长度不及s的话叠加到s的二倍,如果还没有的话,就没必要了,直接判否。

#include
using namespace std;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		string s,x;
		cin>>x>>s;
		if(x.find(s)!=string::npos)
		{
			printf("0\n");
			continue;
		}
		x += x;//一定要先加,因为可能出现x远长于s,但是需要叠一次才出现s
		int c=1,flag=0;
		while(1)
		{
			
			if(x.find(s)!=string::npos) 
			{
				flag=1;
				break;
			}
			if(x.size()<=2*s.size()) //不带等号可能出现卡在这儿刚好超时
			{
				x+=x;
				c++;
				continue;
			}
			if(x.size()>2*s.size()) break;
		}
		if(flag) printf("%d\n",c);
		else printf("-1\n");
	}
}

ps:kmp可以用string的find(s,index)代替,如果find()超时可以用字符串hash,时间复杂度差不多。

Three Threadlets

题目大意:有三条线和一把剪刀,我们每次可以将一条线切成整数长度的两段,问最多切三次,能否使所有线段等长。

思路:最多切三次,那么最短的那一段肯定不能切,应该使所有的都与最短的那个等长。如果不等长,那么如果要实现,其他的长度应该是它的倍数,而且我们讨论一下可以发现,如果是两倍就切一刀,三倍切两刀,四倍切三刀,所以我们只用看剩下线与最短的线的关系即可。

#include
using namespace std;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int a[4];
		int mi=1e9+7;
		for(int i=0;i<3;i++)
		{
			scanf("%d",&a[i]);
			mi=min(mi,a[i]);
		}
		int flag=1,c=0;
		for(int i=0;i<3;i++)
		{
			if(a[i]%mi==0) c += a[i]/mi-1;
			else flag=0;
		}
		if(flag&&c<4) printf("YES\n");
		else printf("NO\n");
	}
}

Perfect Square

题目大意:我们定义完美矩阵为顺时针旋转90度后仍然不变的矩阵为完美矩阵,先有一个矩阵,我们可以将其中的一个字母替换成它在字母表中的下一个字母(如果为z不会更改),这个操作可以进行若干次。问最少进行多少次可以得到完美矩阵。

思路:我们可以发现完美矩阵的特点,即a[i][j]==a[j][n-i+1]==a[n-i+1][n-j+1]==a[n-j+1][i],那么我们只用找到这四个元素,然后将所有元素都变成最大的那个即可。

#include
using namespace std;
char a[1010][1010];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%s",a[i]+1);
		long long ans=0;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(a[i][j]==a[j][n-i+1]&&a[n-i+1][n-j+1]==a[n-j+1][i]&&a[j][n-i+1]==a[n-i+1][n-j+1]) continue;
				mapmp;
				mp[a[i][j]]++;
				mp[a[j][n-i+1]]++;
				mp[a[n-i+1][n-j+1]]++;
				mp[a[n-j+1][i]]++;
				char c;
				for(auto it:mp) c=it.first;
				ans-=(a[i][j]-c)+(a[j][n-i+1]-c)+(a[n-i+1][n-j+1]-c)+(a[n-j+1][i]-c);
				a[i][j]=a[j][n-i+1]=a[n-i+1][n-j+1]=a[n-j+1][i]=c;
			}
		}
		printf("%lld\n",ans);
	}
}

Divide and Equalize

思路:现给定一个数组a[],我们可以进行如下操作:

选择ai,aj,x,要求ai%x==0,然后我们需要用ai/x替换ai,x*aj替换aj。

问能否通过若干次操作使数组中的数全部相等。

思路:我们来分析一下,可以发现,这道题实际上相当于ai失去一个因数x,aj得到一个因数x,x在数组内部守恒,同时最后相等,那么是不是可以说,大家有的x的个数是一样的。x代表因数,那么我们联想到质因数,那么就是最后大家的每个质因数的个数相同。我们只需要分解质因数,统计每个质因数出现的次数,如果每个质因数出现次数都可以整除数组个数,那么就是可以的,否则就不行。这里引入线性筛质数的方法进行预处理优化。

#include
using namespace std;
vectorp;
int st[1000020];
signed main()
{
	for(int i=2;i<=1000000;i++)
	{
		if(!st[i]) p.push_back(i);
		for(auto j:p)
		{
			//j>1000000/i i>1000000/j?
			if(j>1000000/i) break;
			st[i*j]=1;
			if(i%j==0) break;
		}
	}
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		mapmp;
		for(int i=1;i<=n;i++)
		{
			int x;
			scanf("%d",&x);
			for(auto it:p)
			{
				if(x==1) break;
				while(x%it==0)
				{
					mp[it]++;
					x/=it;
				}
			}
		}
		int flag=1;
		for(auto it:mp)
		{
			if(it.second%n) 
			{
				flag=0;
				break;
			}
		}
		if(flag) printf("YES\n");
		else printf("NO\n");
	}
}

Block Sequence

题目大意:我们给定一个大小为n的数组a[],现在定义美丽数组为,该数组可以分成若干块,每一块的第一个元素的值都是该块中剩下元素的个数。我们可以删除一些元素,问最少删除多少个元素可以得到想要的数组。

思路:我们来考虑,一个元素能否作为开头,取决于它后面有多少元素,如果它后面的元素的个数小于它的值,那么肯定是不可以的,那么就需要删除。但是还要考虑到一点,如果后面有已经成块的,我们就要考虑,这种情况该如何处理,我们来看,假设当前已经访问到a[i]了,后面的元素个数大于a[i],我们如果以a[i]作为开头,划定一块元素出来,那么剩下的元素就要看能不能成块,不能自然全部删掉,假设划出k个元素,那么就要看以a[i+k]作为开头的时候需要删除几个元素,这样就有了递推式。我们想要的答案实际就是d[1],因为我们是通过累计得到的。

递推公式:

d[i]=min(d[i+1]+1,d[i+a[i]+1]);//a[i]<=n-i

d[i]=d[i+1]+1;//a[i]>n

//有时候删掉可能会更优

#include
using namespace std;
int a[200010],d[200010];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		memset(d,0,sizeof d);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
		}
		d[n]=1;
		for(int i=n-1;i>=1;i--)
		{//有可能删掉更优
			d[i]=d[i+1]+1;
			if(a[i]<=n-i)  d[i]=min(d[i],d[i+a[i]+1]);
		}
		printf("%d\n",d[1]);
	}
}

Minimum Maximum Distance

题目大意:给定一棵树,标记树上的一些点,fi表示i到标记点的最大距离,我们要求出fi的最小值。

Codeforces Round 903 (Div. 3)补题_第1张图片

思路:我们想一想,最小值,很明显两个标记点之间的点产生的fi会比路径之外的要小一些,另外我们还要保证中间的点到左右两端的标记点的距离之一是fi,那么这两个标记点之间的距离一定要是所有标记点中最大 的。这个怎么找呢?先从任意一点出发,找到最远的,再从那个最远的出发找到最远的,这样得到的就是距离最远的两个标记点了。如果仅有一个标记点,那么答案就是0,因为它本身到本身是最小的。边的权值都是1,所以我们实际上找到这个最大距离后就可以直接得出答案了,具体i是哪个点并不重要。

#include
using namespace std;
const int N=200010;
int h[N],e[2*N],ne[2*N],idx;
int ans,mx,in;
int st[N],vis[N],b[N],a[N];
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int x,int k)
{
	if(k>mx&&vis[x]) 
	{
		mx=k,in=x;
	}
	st[x]=1;
	for(int i=h[x];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(!st[j]) dfs(j,k+1);
	}
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		memset(vis,0,sizeof vis);
		for(int i=1;i<=m;i++) scanf("%d",&b[i]),vis[b[i]]=1;
		memset(h,-1,sizeof h);
		for(int i=1;i

ps:一定要记得还原状态。

恭喜你成功战胜低级欲望!!!记住现在的高兴与满足。

是否成就只与方法是否正确和努力是否足够有关,剩下的,花一定会开。

你可能感兴趣的:(codeforces补题,哈希算法,散列表,算法)