目录
C. Nezzar and Symmetric 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;
}
结语
打竞赛需要能够静下心来思考。
重在思路,次在代码具体实现。
代码能不能看懂不重要,思路想明白了才是最重要的。
如有错误,欢迎指正,您的批评将是我前进的动力。
加油加油~
写到凌晨两点不容易,喜欢了就点个赞再走叭