题面传送门
这个构造还是很巧妙的。
有一个显然的性质:序列中所有数的异或和等于最后只有一种数时所有数的异或和。
还有一个结论:当有三个数
a
,
b
,
b
a,b,b
a,b,b时,只要一次操作就能使其变成三个
a
a
a。
那么事实上无解的情况已经呼之欲出了:当
n
m
o
d
2
=
1
n\bmod 2=1
nmod2=1且序列中的所有数异或和不为
0
0
0即无解。
现在主要是三步:处理出所有数得异或和,令其为
a
n
a_n
an,将剩余
n
−
1
n-1
n−1个数两两相等。再用
n
2
\frac{n}{2}
2n次操作将其变为一样。
对于
n
n
n为奇数的情况,我们只要对于每个
i
=
2
k
+
1
i=2k+1
i=2k+1做一次
(
i
,
i
+
1
,
i
+
2
)
(i,i+1,i+2)
(i,i+1,i+2)的操作即可,这样
a
i
+
2
a_{i+2}
ai+2处即为前
i
+
2
i+2
i+2个数的异或和。且前
i
+
1
i+1
i+1个数任意一个位置
k
k
k都与
k
⨁
1
k\bigoplus1
k⨁1相等。这样一边就处理出了前两步。
对于
n
n
n为偶数的情况,则最后的序列中的数是什么都可以。不妨令其为前
n
−
1
n-1
n−1个数的异或和。这在有解的情况下其实就等于
a
n
a_n
an,所以只要仿照上面做一遍即可。
总共做了
n
−
1
n-1
n−1次操作。复杂度
O
(
n
)
O(n)
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]);
}