【学军NOIP开放题2-D】均分(构造)(分类讨论)

均分

题目链接:学军NOIP开放题2-D

题目大意

要你给 n 个数赋值,然后使得每个数满足它标识数对应的条件。
标识数是 0 则剩下的 n-1 个数不能分成和相同的两堆,是 1 就是可以。
然后判断无解,或者给出构造方案。

思路

我们考虑根据这个大致思路来构造,就是尽可能的让不可以分成的和是奇数。

然后开始分类讨论:(下面让 c 0 , c 1 c0,c1 c0,c1 为标识符为 0 , 1 0,1 0,1 的个数)

首先判掉两个特殊的,就是只有 0 0 0 1 1 1

只有 0 0 0 就如果 c 1 c1 c1 是偶数就全都是 1 1 1,如果是奇数你可以 c 1 − 2 c1-2 c12 1 1 1,然后一个 10000 10000 10000 一个 9999 9999 9999

只有 1 1 1 就如果 c 0 c0 c0 是奇数就全是 1 1 1,否则就无解。
无解这里可以证一下,首先如果所有数的 gcd ⁡ > 1 \gcd>1 gcd>1 我们可以同除效果不变。

那因为你要全部有解,那一定至少总和一定要都是偶数,所以所有数奇偶性相同。
如果都是偶数就可以同除 2 2 2,那如果是奇数那总和就一定是奇数(奇数*奇数=奇数),所以就无解了。

然后再判掉两个: c 0 = 1 , c 1 = 1 c0=1,c1=1 c0=1,c1=1

c 0 = 1 c0=1 c0=1 的话如果 c 1 = 2 c1=2 c1=2 就无解,否则我们可以这样构造:
0 0 0 的那个放 n − 2 n-2 n2,然后 1 1 1 中放一个 2 n − 5 2n-5 2n5,其它都放 1 1 1

c 1 = 1 c1=1 c1=1 的时候 1 1 1 的位置放 1 1 1 0 0 0 的放 c 0 − 1 c0-1 c01 2 2 2,一个 2 ∗ c 0 − 2 2*c0-2 2c02

然后再按 c 0 , c 1 c0,c1 c0,c1 的奇偶分类讨论。

如果 c 1 c1 c1 是奇数,那就好办了,直接 1 1 1 的填 1 1 1 0 0 0 的填 2 2 2

如果 c 1 c1 c1 是偶数,我们就看 c 0 c0 c0
如果 c 0 c0 c0 是偶数,那也好办 1 1 1 的填 2 2 2 0 0 0 的填 1 1 1
如果 c 1 c1 c1 是奇数,我们可以这样: 0 0 0 里面一个 100 100 100,一个 2 2 2,剩下的都是 1 1 1,然后 1 1 1 里面都是所有 0 0 0 填的数的和。

这些自己证明一下都可以证出来。
然后就可以了。

代码

#include<cstdio>
#include<cstring>

using namespace std;

int T, c0, c1, n, ans[51];
int pl0[51], pl1[51];
char s[101];

int main() {
    
    
//	freopen("div.in", "r", stdin);
//	freopen("div.out", "w", stdout);
	
	scanf("%d", &T);
	while (T--) {
    
    
		scanf("%d", &n);
		scanf("%s", s + 1);
		c0 = c1 = 0;
		for (int i = 1; i <= n; i++)
			if (s[i] == '0') c0++, pl0[c0] = i;
				else c1++, pl1[c1] = i;
		
		if (c0 == 0) {
    
    
			if (c1 & 1) {
    
    
				printf("Yes\n");
				for (int i = 1; i <= n; i++)
					printf("1 ");
				printf("\n");
			}
			else printf("No\n");
			continue;
		}
		if (c1 == 0) {
    
    
			if (c0 & 1) {
    
    
				printf("Yes\n");
				printf("10000 9999 ");
				for (int i = 3; i <= n; i++) printf("1 ");
				printf("\n");
			}
			else {
    
    
				printf("Yes\n");
				for (int i = 1; i <= n; i++) printf("1 ");
				printf("\n");
			}
			continue;
		}
		if (c0 == 1 && (n & 1)) {
    
    
			if (c1 == 2) printf("No\n");
				else {
    
    
					printf("Yes\n");
					ans[pl0[1]] = n - 2;
					ans[pl1[1]] = n - 2 + n - 3;
					for (int i = 2; i <= c1; i++) ans[pl1[i]] = 1;
					for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
					printf("\n");
				}
			continue;
		}
		if (c1 == 1) {
    
    
			printf("Yes\n");
			ans[pl1[1]] = 1;
			for (int i = 1; i < c0; i++) ans[pl0[i]] = 2;
			ans[pl0[c0]] = 2 * c0 - 2;
			for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
			printf("\n");
			continue;
		}
		if (c1 & 1) {
    
    
			printf("Yes\n");
			for (int i = 1; i <= c1; i++) ans[pl1[i]] = 1;
			for (int i = 1; i <= c0; i++) ans[pl0[i]] = 2;
			for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
			printf("\n");
		}
		else {
    
    
			printf("Yes\n");
			if (c0 & 1) {
    
    
				int sum = 0;
				ans[pl0[1]] = 100; ans[pl0[2]] = 2; sum = 102;
				for (int i = 3; i <= c0; i++) ans[pl0[i]] = 1, sum++;
				for (int i = 1; i <= c1; i++) ans[pl1[i]] = sum;
			}
			else {
    
    
				for (int i = 1; i <= c0; i++) ans[pl0[i]] = 1;
				for (int i = 1; i <= c1; i++) ans[pl1[i]] = 2;
			}
			for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
			printf("\n");
		}
	}
	
	return 0;
}

おすすめ

転載: blog.csdn.net/weixin_43346722/article/details/121388446