CF1438D

首先,要发现两个性质。
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;
}
posted @ 2021-01-12 13:07  BBD186587  阅读(88)  评论(0编辑  收藏  举报