Codeforces Round #632 (Div. 2)

Codeforces Round #632 (Div. 2)

CF1333A Little Artem

知识点:

构造

题意:

多次询问一个\(n\times m\)的网格,每个格子填\(0,1\),要求有\(0\)相邻的\(1\)的个数是有\(1\)相邻的\(0\)的个数\(+1\),求一种方案。

解法:

因为\(n\)\(m\)都不小于\(2\),所以直接构造第一行第一列是\(0\),其他是\(1\)即可。

备注:

#include<bits/stdc++.h>
using namespace std;

int T,n,m;

int main()
{
	int i,j;
	cin>>T;
	while (T--)
	{
		cin>>n>>m;
		for (i=1;i<=n;i++)
		{
			for (j=1;j<=m;j++)
			{
				if (i==1&&j==1)
					putchar('W');
				else
					putchar('B');
			}
			puts("");
		}
	}
}

CF1333B Kind Anton

知识点:

模拟

题意:

给定两个序列,第一个序列的数是\(\{-1,0,1\}\)中的数。一个序列可以操作无限次,每次操作选定不同的两个数\((i,j)\),要求\(i\leq j\ ,\ a_j=a_i+a_j\),问第一个序列是否能变成第二个,多次询问。

解法:

因为后面的数不会对前面产生贡献,所以从后往前模拟一遍,用两个计数器分别记录当前点前面的\(-1,1\)的个数,然后看差值的正负判断即可。

备注:

#include<bits/stdc++.h>
using namespace std;

const int maxn=100010;
int T,n,a[maxn],b[maxn],c[5],ans;

int main()
{
	int i;
	cin>>T;
	while (T--)
	{
		cin>>n;
		memset(c,0,sizeof(c));
		for (i=1;i<=n;i++)
		{
			cin>>a[i];
			c[a[i]+1]++;
		}
		for (i=1;i<=n;i++)
			cin>>b[i];
		ans=1;
		for (i=n;i>=1;i--)
		{
			c[a[i]+1]--;
			if (a[i]==b[i])
				continue;
			int d=b[i]-a[i];
			if (d>0)
			{
				if (c[2]<=0)
				{
					ans=0;
					break;
			}
			}
			if (d<0)
			{
				if (c[0]<=0)
				{
					ans=0;
					break;
				}
			}
		}
		puts(ans?"YES":"NO");
	}
}

CF1333C Eugene and an array

知识点:

计数,前缀和,双指针

题意:

给定一个序列,定义一个序列是好的为这个序列的所有非空子串都不为\(0\)。问一个序列的所有子串中是好的有多少个。

解法:

经过多次尝试(本来我想用一种在\(ABC\)中见过的方法,就是统计不合法的有多少个,但是发现这样去不了重,所以不行),发现肯定是
先求前缀和,假如一个子串是好的,那一定是这个子串中不会出现前缀和相等的数而且当前右端点的\(a_i\)不为\(0\)。发现这个东西是有单调性的,所以用\(map\)维护每个前缀和出现的次数,然后两个指针维护当前的合法区间,是线性的。

备注:

这道题我还\(WA\)了两次,要特别注意当前点的\(a_i\)\(0\)的情况,这种情况直接特判不算进答案里。但是右移指针的方法没变,还是找到第一个令当前前缀和次数为\(1\)为止。因为这个区间是前缀和区间,假如\(l=r\)那么就没有东西了。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=200010;
int n,a[maxn];
ll ans,s[maxn];
map<ll,int>mp;
#define mkp make_pair

int main()
{
	int i,l,r;
	scanf("%d",&n);
	for (i=1;i<=n;i++)
		scanf("%d",&a[i]),s[i]=s[i-1]+a[i];
	if (n==1)
	{
		if (a[1]==0)
			puts("0");
		else
			puts("1");
		return 0;
	}
	l=0;
	mp.insert(mkp(0,1));
	for (r=1;r<=n;r++)
	{
		if (mp.find(s[r])==mp.end())
			mp.insert(mkp(s[r],1));
		else
			mp[s[r]]++;
		/*if (a[r]==0)
		{
			while (l<=r)
			{
				mp[s[l]]--;
				l++;
			}
			continue;
		}*/
		while (mp[s[r]]==2&&l<=r)
		{
			mp[s[l]]--;
			l++;
		}
		if (a[r]!=0)
			ans+=r-l;
	}
	printf("%lld\n",ans);
	return 0;
}

CF1333D Challenges in school №41

知识点:

贪心,逆序对,冒泡排序

题意:

有一个序列,每个位置要么往左看要么往右看,现在每一秒可以让至少一对盯着对方的人同时转头,但是同一秒一个人只能转一次。问恰
\(k\)步使得没有人互相盯着的一种方案。

解法:

显然,在最后肯定是左边一段的人全都看着左边,右边剩下的人都看着右边才能满足条件。于是我们让\(L\)\(0\)\(R\)\(1\)。我们进行一轮冒泡排序,可以让若干对盯着对方的人(也就是逆序对)交换,而且可以发现这些人肯定是不会重复的,而且是这轮中能选的最多的了。然后交换,冒泡直到没有逆序对为止。那么答案的下界就是冒泡的次数,上界就是冒泡影响过的位置的总次数(每一次交换我们都拆开来算一次)。假如答案不在这个区间内,那就无解。否则肯定有解。然后用调整法,让前面若干轮直接冒泡,后面的拆开来一次次地冒泡即可。

备注:

可以先求一个冒泡的前缀和,方便后面统计,而且这道题细节很繁琐。

#include<bits/stdc++.h>
using namespace std;

const int maxn=3010;
int n,k,a[maxn],s[maxn],minv,maxv;
vector<int>v[maxn];
char c[maxn];
#define pb push_back

int main()
{
	int i,j,cnt,cur;
	scanf("%d%d%s",&n,&k,c+1);
	for (i=1;i<=n;i++)
		a[i]=(c[i]=='R');
	for (i=1;i<=n;i++)
	{
		cnt=0;
		for (j=1;j<n;j++)
			if (a[j]>a[j+1])
			{
				++cnt;
				v[i].pb(j);
			}
		maxv+=cnt;
		s[i]=maxv;
		if (!cnt)
		{
			minv=i-1;
			break;
		}
		for (j=0;j<cnt;j++)
			swap(a[v[i][j]],a[v[i][j]+1]);
	}
	if (k<minv||k>maxv)
	{
		puts("-1");
		return 0;
	}
	if (k==minv)
	{
		for (i=1;i<=minv;i++)
		{
			cnt=v[i].size();
			printf("%d ",cnt);
			for (j=0;j<cnt;j++)
				printf("%d ",v[i][j]);
			puts("");
		}
		return 0;
	}
	cur=0;
	while (cur<minv&&maxv-s[cur+1]+cur+1>=k)
		cur++;
	for (i=1;i<=cur;i++)
	{
		cnt=v[i].size();
		printf("%d ",cnt);
		for (j=0;j<cnt;j++)
			printf("%d ",v[i][j]);
		puts("");
	}
	if (maxv-s[cur]+cur-k+1>0)
	{
		cnt=maxv-s[cur]+cur-k+1;
		printf("%d ",cnt);
		for (j=0;j<cnt;j++)
			printf("%d ",v[cur+1][j]);
		puts("");
	}
	cnt=v[cur+1].size();
	for (j=maxv-s[cur]+cur-k+1;j<cnt;j++)
		printf("1 %d\n",v[cur+1][j]);
	for (i=cur+2;i<=minv;i++)
	{
		cnt=v[i].size();
		for (j=0;j<cnt;j++)
			printf("1 %d\n",v[i][j]);
	}
	return 0;
}

CF1333E Road to 1600

知识点:

构造

题意:

解法:

不会

备注:

CF1333F Kate and imperfection

知识点:

数学,\(GCD\),线性筛

题意:

给定一个\(n\),一个集合里有不超过\(n\)的所有正整数。要求对于每一个\(2\)\(n\)\(k\),所有大小为\(k\)的集合中,对于每个集合,两两求出\(GCD\),每个集合的贡献是最大的\(GCD\),而对于每个\(k\)的答案是当前所有集合中的贡献值的最小值。求出所有的贡献最小值。

解法:

首先可以观察到一个性质,假如一个数\(x\)要加入当前某个集合,那么它所有的真因子都肯定在里面。原因是假如不在的话肯定假如那些
真因子更优。而假如之后这个数产生的贡献\(GCD\)最大值肯定是\(x\)的最大真因子。有因为能加入\(x\)了,答案肯定不会变小,所以答案肯
定就是这个最大真因子。而最大真因子等于\(x\)除以最小质因子,也就是线性筛中筛到的第一个质数对应的\(i\),那么那这个来排个序然
后从小到大加入数就是答案了。

备注:

注意:1,线性筛中的\(i\)是否是当前质数的倍数这个要放在处理完因数之后。2,质数个数最好开\(n\)这么大,否则有可能\(RE\)

#include<bits/stdc++.h>
using namespace std;

const int maxn=500010;
int n,m,pr[maxn],mc[maxn];
bool v[maxn];

int main()
{
	int i,j;
	cin>>n;
	for (i=2;i<=n;i++)
	{
		if (!v[i])
		{
			pr[++m]=i;
			mc[i]=1;
		}
		for (j=1;j<=m&&pr[j]*i<=n;j++)
		{
			v[pr[j]*i]=1;
			mc[pr[j]*i]=i;
			if (i%pr[j]==0)
				break;
		}
	}
	sort(mc+2,mc+n+1);
	for (i=2;i<=n;i++)
		printf("%d ",mc[i]);
	puts("");
	return 0;
}
posted @ 2020-04-09 20:44  MN2016  阅读(178)  评论(0编辑  收藏  举报