Codeforces Round #698 (Div. 2)-C. Nezzar and Symmetric Array-题解

Codeforces Round #698 (Div. 2)-C. Nezzar and Symmetric Array

传送门
Time Limit: 2 seconds
Memory Limit: 512 megabytes

Problem Description

Long time ago there was a symmetric array a 1 , a 2 , … , a 2 n a_1,a_2,\ldots,a_{2n} a1,a2,,a2n consisting of 2 n 2n 2n distinct integers. Array a 1 , a 2 , … , a 2 n a_1,a_2,\ldots,a_{2n} a1,a2,,a2n is called symmetric if for each integer 1 ≤ i ≤ 2 n 1 \le i \le 2n 1i2n, there exists an integer 1 ≤ j ≤ 2 n 1 \le j \le 2n 1j2n such that a i = − a j a_i = -a_j ai=aj.

For each integer 1 ≤ i ≤ 2 n 1 \le i \le 2n 1i2n, Nezzar wrote down an integer d i d_i di equal to the sum of absolute differences from a i a_i ai to all integers in a a a, i. e. d i = ∑ j = 1 2 n ∣ a i − a j ∣ d_i = \sum_{j = 1}^{2n} {|a_i - a_j|} di=j=12naiaj.

Now a million years has passed and Nezzar can barely remember the array d d d and totally forget a a a. Nezzar wonders if there exists any symmetric array a a a consisting of 2 n 2n 2n distinct integers that generates the array d d d.

Input

The first line contains a single integer t t t ( 1 ≤ t ≤ 1 0 5 1 \le t \le 10^5 1t105) — the number of test cases.

The first line of each test case contains a single integer n n n ( 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105).

The second line of each test case contains 2 n 2n 2n integers d 1 , d 2 , … , d 2 n d_1, d_2, \ldots, d_{2n} d1,d2,,d2n ( 0 ≤ d i ≤ 1 0 12 0 \le d_i \le 10^{12} 0di1012).

It is guaranteed that the sum of n n n over all test cases does not exceed 1 0 5 10^5 105.

Output

For each test case, print “YES” in a single line if there exists a possible array a a a. Otherwise, print “NO”.

You can print letters in any case (upper or lower).

Sample Input

6
2
8 12 8 12
2
7 7 9 11
2
7 11 7 11
1
1 1
4
40 56 48 40 80 56 80 48
6
240 154 210 162 174 154 186 240 174 186 162 210

Sample Onput

YES
NO
NO
NO
NO
YES

Note

In the first test case, a = [ 1 , − 3 , − 1 , 3 ] a=[1,-3,-1,3] a=[1,3,1,3] is one possible symmetric array that generates the array d = [ 8 , 12 , 8 , 12 ] d=[8,12,8,12] d=[8,12,8,12].

In the second test case, it can be shown that there is no symmetric array consisting of distinct integers that can generate array d d d.


题目大意

原来有2n个数,各不相同,这2n个数中,每个数的相反数也在其中。
Nezzar在100万年前计算出了 每个数与其他数的差值 的和,但忘掉了原来那2n个数。
问从这2
n个计算出来的差值和能不能推出一个满足条件的2*n个数。


解题思路

首先原数组中,一个数的差值和 与 这个数的相反数的差值和 是相同的。

举个例子,假如原来这6个数是 ±2、±7、±9
那么

原数差值和
-236
236
-746
746
-954
954

-2的差值和就是abs(2-(-2)) + abs(-7-(-2)) + abs(7-(-2)) + abs(-9-(-2)) + abs(9-(-2))

这就需要 条件一 差值和们 成对出现

因±差值和相同,故先只研究正值

先研究2:
2与-2的差值是两个2(2的两倍)
2与±7的差值和是两个7(7的两倍)
2与±9的差值和是两个9(9的两倍)

再研究7:
7与±2的差值和是两个7(7的两倍)
7与-7的差值是两个7(7的两倍)
7与±9的差值和是两个9(9的两倍)

最后研究9:
9与±2的差值和是两个9(9的两倍)
9与±7的差值和是两个9(9的两倍)
9与-9的差值是两个9(9的两倍)

所以有

差值和来历
362*(2+7+9)
462*(7+7+9)
542*(9+9+9)
这就需要 条件二 差值和都为偶数

并且由此,我们可以分别计算出每一个原来的数。

在计算过程中,每一个计算出的原来的数需要满足一下条件:

条件三 原来的数是整数(可以整除)
条件四 原来的数各不相同

但是这样交上去还是没有考虑完所有的情况,还有一点就是计算出的原来的数的范围:>1
这是因为前面我们是只考虑整数才得出的结论,所以如果计算过程中出现了负数(或0),就不可以继续还原。

这就需要 条件五 计算过程中的数都是正数

总结

条件一:差值和们 成对出现
条件二:差值和都为偶数
条件三:原来的数是整数(可以整除)
条件四:原来的数各不相同
条件五:计算过程中的数都是正数

其中,条件三和条件五可以合并为

计算过程中的数都是正整数

总之,只需要满足

条件一:差值和们 成对出现
条件二:差值和都为偶数
条件三:计算过程中的数都是正整数


AC代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll really[100010]; // Really 记录真正需要处理的数(表格二左边的1/2)
int main()
{
	int N;
	cin >> N;
	while (N--)
	{
		int n;
		scanf("%d", &n);
		bool buxing = 0; //不行的拼音
		map<ll, int> ma; //用map记录出现了几次(条件一)
		for (int i = 0; i < 2 * n; i++)
		{
			ll t;
			scanf("%lld", &t);
			if (t % 2) //差值和是否为偶数(条件二)
				buxing = 1;
			ma[t]++;
		}
		int sum = 1;
		ll realOriSum = 0;  // 已经得出的原来的数,上文样例中便是 9  7  3
		if (buxing)
		{
			puts("NO");
		}
		else
		{
			for (map<ll, int>::iterator it = ma.begin(); it != ma.end(); it++)
			{
				if (it->second != 2) // 差值和是否都出现了两处(条件一)
					buxing = 1;
				really[sum++] = (it->first) / 2;
			}
			if (buxing)
			{
				puts("NO");
			}
			else
			{
				for (sum--; sum > 0; sum--)
				{
					ll all = really[sum] - realOriSum;  //减去原来的数
					if (all % sum)  //是否可以整除
						buxing = 1;
					ll thisReal = all / sum;
					realOriSum += thisReal;
					if (thisReal <= 0)
						buxing = 1;
				}
				if (buxing)
				{
					puts("NO");
				}
				else
					puts("YES");
			}
		}
	}
	return 0;
}

2021-1-29更

如果没看懂

上文中的例子原始数组±2、±7、±9
那么题目要给的差值和为 36 36 46 54 46 54(顺序不一定)
去重排序除以2(用really数组保存)18 23 27
27/3=9,所以原始数组中最大的数就是9
(23-9)/2=7,所以原始数组中还有一个正数7
(18-9-7)/1=2,所以原始数组中还有一个正数2

所以原始数组为±2、±7、±9,输出YES

注释简化版代码

/*CSDN简化版本*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;  // 用ll来代替long long
/*really[]
	题目给的是计算出的差值和,是无序且重复的
	这个really数组就是用来有序地记录这些差值和的一半的
	比如上文所举的例子,±2、±7、±9
	那么题目所给的差值和就是 36 36 46 46 54 54
	而really数组中要记录的就是 18 23 27 (差值和的一半)
	除以二是因为表格二,差值和都是原数和的两倍
*/
ll really[100010]; 
int main()
{
	int N;
	cin >> N;  //共有N个test cases
	while (N--)
	{
		int n;
		scanf("%d", &n);
		bool buxing = false; //“不行”的拼音,一旦为true就说明这组样例“不行”
		map<ll, int> ma; //用map记录出现了几次(条件一)
		for (int i = 0; i < 2 * n; i++)
		{
			ll t;
			scanf("%lld", &t);
			if (t % 2) //差值和是否为偶数(条件二)
				buxing = 1; //如果t%2有余数,则说明这个数是奇数
			ma[t]++; //t出现的次数加一
		}
		int sum = 1; //really数组中需要放置的数的下标,笔者选择从下标1开始存放
		ll realOriSum = 0; // 这是计算出来的原数组中的数,上文样例中便是 9  7  3
		/*ma的迭代器,这个for循环从小到大遍历了插入ma中的差值和*/
		for (map<ll, int>::iterator it = ma.begin(); it != ma.end(); it++)
		{
			/*it->second就是这个差值和出现的次数*/
			if (it->second != 2) // 差值和是否都出现了两处(条件一)
				buxing = 1;
			/*it->first是这个差值和*/
			really[sum++] = (it->first) / 2; // 差值和的一半存放到really数组中
		}
		for (sum--; sum > 0; sum--)
		{
			ll all = really[sum] - realOriSum; //减去原来的数
			if (all % sum)	//是否可以整除
				buxing = 1;
			ll thisReal = all / sum; // 这个是原始数组(计算差值和之前那个±2±7±9的数组)
			realOriSum += thisReal; // 求出的原始的数的和
			if (thisReal <= 0)  // 按上述方法,原来的数必须是正数
				buxing = 1;
		}
		if (buxing) //如果不行buxing就会为true,这个样例从头到尾都可以,buxing就还是原来初始的false
			puts("NO");
		else //否则就可以
			puts("YES");
	}
	return 0;
}

结语

打竞赛需要能够静下心来思考。
重在思路,次在代码具体实现。
代码能不能看懂不重要,思路想明白了才是最重要的。
如有错误,欢迎指正,您的批评将是我前进的动力。
加油加油~

写到凌晨两点不容易,喜欢了就点个赞再走叭

评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tisfy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值