CF1438D Powerful Ksenia
题面传送门
这个构造还是很巧妙的。
有一个显然的性质:序列中所有数的异或和等于最后只有一种数时所有数的异或和。
还有一个结论:当有三个数\(a,b,b\)时,只要一次操作就能使其变成三个\(a\)。
那么事实上无解的情况已经呼之欲出了:当\(n\bmod 2=1\)且序列中的所有数异或和不为\(0\)即无解。
现在主要是三步:处理出所有数得异或和,令其为\(a_n\),将剩余\(n-1\)个数两两相等。再用\(\frac{n}{2}\)次操作将其变为一样。
对于\(n\)为奇数的情况,我们只要对于每个\(i=2k+1\)做一次\((i,i+1,i+2)\)的操作即可,这样\(a_{i+2}\)处即为前\(i+2\)个数的异或和。且前\(i+1\)个数任意一个位置\(k\)都与\(k\bigoplus1\)相等。这样一边就处理出了前两步。
对于\(n\)为偶数的情况,则最后的序列中的数是什么都可以。不妨令其为前\(n-1\)个数的异或和。这在有解的情况下其实就等于\(a_n\),所以只要仿照上面做一遍即可。
总共做了\(n-1\)次操作。复杂度\(O(n)\)
代码实现:
#include<cstdio>
using namespace std;
int n,m,k,x,y,z,a[1000039],ans,tot,pus;
int head,xs[1000039],ys[1000039],zs[1000039];
int main(){
register int i;
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&a[i]),ans^=a[i];
if(n%2==0&&ans) {printf("NO\n");return 0;}
for(i=3;i<=n;i+=2) xs[++head]=i-2,ys[head]=i-1,zs[head]=i;
for(i=2;i<n;i+=2) xs[++head]=i-1,ys[head]=i,zs[head]=n;
printf("YES\n%d\n",head);
for(i=1;i<=head;i++) printf("%d %d %d\n",xs[i],ys[i],zs[i]);
}