本文已参与「新人创作礼」活动,一起开启掘金创作之路。
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 consisting of distinct integers. Array is called symmetric if for each integer , there exists an integer such that .
For each integer , Nezzar wrote down an integer equal to the sum of absolute differences from to all integers in , i. e. .
Now a million years has passed and Nezzar can barely remember the array and totally forget . Nezzar wonders if there exists any symmetric array consisting of distinct integers that generates the array .
Input
The first line contains a single integer () — the number of test cases.
The first line of each test case contains a single integer ().
The second line of each test case contains integers ().
It is guaranteed that the sum of over all test cases does not exceed .
Output
For each test case, print "YES" in a single line if there exists a possible array . 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, is one possible symmetric array that generates the array .
In the second test case, it can be shown that there is no symmetric array consisting of distinct integers that can generate array .
题目大意
原来有2n个数,各不相同,这2n个数中,每个数的相反数也在其中。 Nezzar在100万年前计算出了 每个数与其他数的差值 的和,但忘掉了原来那2n个数。 问从这2n个计算出来的差值和能不能推出一个满足条件的2*n个数。
解题思路
首先原数组中,一个数的差值和 与 这个数的相反数的差值和 是相同的。
举个例子,假如原来这6个数是 ±2、±7、±9 那么
| 原数 | 差值和 |
|---|---|
| -2 | 36 |
| 2 | 36 |
| -7 | 46 |
| 7 | 46 |
| -9 | 54 |
| 9 | 54 |
| -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的两倍)
所以有
| 差值和 | 来历 |
|---|---|
| 36 | 2*(2+7+9) |
| 46 | 2*(7+7+9) |
| 54 | 2*(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;
}
结语
打竞赛需要能够静下心来思考。 重在思路,次在代码具体实现。 代码能不能看懂不重要,思路想明白了才是最重要的。 如有错误,欢迎指正,您的批评将是我前进的动力。 加油加油~
写到凌晨两点不容易,喜欢了就点个赞再走叭
同步发文于我的CSDN