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

目录

  • C. Nezzar and Symmetric Array
  • 题目大意
  • 解题思路
    • 首先原数组中,一个数的差值和 与 这个数的相反数的差值和 是相同的。
          • 这就需要 *条件一* 差值和们 成对出现
    • 因±差值和相同,故先只研究正值
          • 这就需要 *条件二* 差值和都为偶数
    • 并且由此,我们可以分别计算出每一个原来的数。
          • *条件三* 原来的数是整数(可以整除)
          • *条件四* 原来的数各不相同
          • 这就需要 *条件五* 计算过程中的数都是正数
  • 总结
  • AC代码
  • 如果没看懂
      • 注释简化版代码
  • 结语

C. Nezzar and Symmetric Array

题目大意

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

结语

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

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

你可能感兴趣的:(CodeForces,题解)