Loading

7382. 【2021.11.13NOIP提高组联考】划分

Description

\(n\) 个砝码,第 \(i\) 个砝码质量为 \(a_i\) 克。但砝码的质量看不清了,你只有一个长为 \(n\) 的 01 串 \(S\) 作为信息。\(S_i=1\) 当且仅当除了第 \(i\) 个砝码外的 \(n − 1\) 个砝码可以恰好划成两个质量和相同的组。给出一个合法的方案,或声明无解。本题有多组数据。你构造的质量值域应为 \([1,10^4 ]\)

\(n\le 50\)

Solution

十分有趣的构造题,方法很多,这里提供一种方法。

设 01 串中,0 的个数为 \(c0\),1 的个数为 \(c1\)。先讨论最特殊的两种情况,\(c0=0\) 或者 \(c1=0\)

\(c0=0\) 时,也就是字符串只由 1 构成,此时如果当 \(n\) 是奇数,全部输出 1 就可以了。反之,就无解,具体证明如下:

对于任意合法方案, 把所有 \(a_{i}\) 除以它们的最大公约数, 能转化到全体 \(a_{i}\) 最大公约数为 1 。

记总质量为 \(M\), 有解的必要条件是 \(M-a_{i}\) 均为偶数, \(a_{i}\) 必须奇偶性 相同,又不能全是偶数,那么 \(a\) 全是奇数。

此时 \(M-a_{i}\) 为奇数, 矛盾!所以无解。

\(c1=0\) 时,如果 \(n\) 是偶数,全输出 1 依旧合法,如果 \(n\) 是奇数,前 13 个输出 2 的若干次方,\(n>13\) 之后就全部输出 10000 即可。

再讨论一种特殊情况,\(c0=1,c1=n-1\)。如果 \(n\) 是偶数。这种情况下 1 的位置全部输出 1,0 的位置输出 2。如果 \(n\) 是奇数,在 \(n=3\) 的情况下无解,否则只用输出 3 在 0 的位置,再输出 \(n-2\) 个 1,和 1 个 \(n\)

除去以上几种情况,其余情况全部有解。

此时分 \(c1,c2\) 的奇偶性来讨论:

  1. \(c1\) 奇,\(c0\) 奇。1 的位置输出 \(c1\) 个 10000,0 的位置输出 1 个 2 和 \(c0-1\) 个 1。
  2. \(c1\) 奇,\(c0\) 偶。1 的位置输出 \(c1\) 个 10000,0 的位置输出 \(c0\) 个 1。
  3. \(c1\) 偶,\(c0\) 奇。1 的位置输出 \(c1\)\(2\times c0\),0 的位置输出 1 个 \(c0\),1 个 2,\(c0-2\) 个 1。
  4. \(c1\) 偶,\(c0\) 偶。设 \(x\) 为最接近 10000 的 \(c0\) 的倍数,则 1 的位置输出 \(c1\)\(x\),0 的位置输出 \(c0\)\(\frac{x}{c0}\)

Code

#include<cstdio>
#define N 55
using namespace std;
int T,n,c0,c1,res,a[N];
bool flag;
char s[N];
int main()
{
	freopen("div.in","r",stdin);
	freopen("div.out","w",stdout);
	scanf("%d",&T);
	while (T--)
	{
		c0=c1=0;
		scanf("%d",&n);
		scanf("%s",s+1);
		for (int i=1;i<=n;++i)
		{
			a[i]=s[i]-'0';
			if (s[i]=='0') c0++;
			else c1++;
		}
		if (c1==0)
		{
			printf("Yes\n");
			if (n%2==0)
			{
				for (int i=1;i<=n;++i)
					printf("1 ");
			}
			else
			{
				res=1;
				for (int i=1;i<=n;++i)
				{
					if (res<=10000)	printf("%d ",res),res*=2;
					else printf("10000 ");
				}
			}
			printf("\n");
			continue;
		}
		if (c0==0&&n%2==1)
		{
			printf("Yes\n");
			for (int i=1;i<=n;++i)
				printf("1 ");
			printf("\n");
			continue;
		}
		if (c0==0&&n%2==0)
		{
			printf("No\n");
			continue;
		}
		if (c0==1&&c1>=2&&n%2==0)
		{
			printf("Yes\n");
			for (int i=1;i<=n;++i)
			{
				if (a[i]) printf("1 ");
				else printf("2 ");
			}
			printf("\n");
			continue;
		}
		if (c0==1&&c1>=2&&n%2==1)
		{
			if (n==3) printf("No\n");
			else
			{
				printf("Yes\n");
				flag=false;
				for (int i=1;i<=n;++i)
				{
					if (!a[i]) printf("3 ");
					else
					{
						if (!flag) printf("%d ",n),flag=true;
						else printf("1 ");
					}
				}
			}
			printf("\n");
			continue;
		}
		printf("Yes\n");
		if (c1%2==1)
		{
			if (c0%2==1)
			{
				flag=false;
				for (int i=1;i<=n;++i)
				{
					if (a[i]) printf("10000 ");
					else
					{
						if (!flag) printf("2 "),flag=true;
						else printf("1 ");
					}
				}
			}
			else
			{
				for (int i=1;i<=n;++i)
				{
					if (a[i]) printf("10000 ");
					else printf("1 ");
				}
			}
		}
		else
		{
			if (c0%2==0)
			{
				int x=10000-10000%c0;
				for (int i=1;i<=n;++i)
				{
					if (a[i]) printf("%d ",x);
					else printf("%d ",x/c0);
				}
			}
			else
			{
				bool f1=false,f2=false;
				for (int i=1;i<=n;++i)
				{
					if (a[i]) printf("%d ",c0*2);
					else
					{
						if (!f1) printf("%d ",c0),f1=true;
						else if (!f2) printf("2 "),f2=true;
						else printf("1 ");
					}
				}
			}
		}
		printf("\n");
	}
	return 0;
}
posted @ 2021-11-14 20:15  Thunder_S  阅读(45)  评论(0编辑  收藏  举报