首页 > 其他 > 详细

CF1438D

时间:2021-01-12 16:42:50      阅读:23      评论:0      收藏:0      [点我收藏+]

首先,要发现两个性质。
1.一次操作其实相当于把三个数变为相同的一个数。
2.每次操作前后整个数列的异或和不变。
第一个性质比较显然,证明一下第二个性质。
设原来这三个数分别为 \(x\)\(y\)\(z\)
操作前,它们的异或和为 \(x \oplus y \oplus z\)
操作后,这三个数都变成了 \(x \oplus y \oplus z\)
它们的异或和仍然为 \(x \oplus y \oplus z\)

先考虑数列长度为奇数的情况。
首先,把相邻的三个数操作一下,每次操作的起点即为上次操作的终点。
操作后,整个数列就变为了若干段长度为2的相同的数,以及最后一段长度为3的相同的数。
这样,把最后一个数拿出来,与前面的每两个相同的数都做一次操作即可。
操作次数为 \(n-2\) 次,可以通过。

接下来,考虑数列长度为偶数的情况。
如果是有解的,且数列长度为偶数,那么最后整个数列的异或和一定为0。
根据上面的性质2,可以得到数列操作前的异或和也一定为0,同理,整个数列在每一步操作后的异或和也为0。
现在已经可以判断无解了。
若有解,考虑把最后一个数单独拿出来,把前面的 \(n-1\) 个数按照奇数的做法做一遍。
因为整个数列在每一步操作后的异或和一定为0,又因为数列的前 \(n-1\) 个数已经相等了,所以,它们也一定和最后一个数相等。
操作次数为 \(n-3\) 次。

#include<iostream>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int N=1e5+10;
int n,a[N],sum;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum^=a[i];
	if(n%2==0&&sum) puts("NO"),exit(0);
	if(n%2==0) n--;
	printf("YES\n%d\n",n-2);
	for(int i=1;i<=n-2;i+=2) printf("%d %d %d\n",i,i+1,i+2);
	for(int i=1;i<=n-4;i+=2) printf("%d %d %d\n",i,i+1,n);
	return 0;
}

CF1438D

原文:https://www.cnblogs.com/zhs1/p/14266454.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!