题意
给你一个长度为n的数组,只由0和1构成,你可以进行如下操作,从1~n-2中选择一个下标i,使得a[i],a[i+1],a[i+2]同时变为a[i] xor a[i+1] xor a[i+2],要求将数组中的数字全部变为0。
思路
首先数字不能全部为1,全部为1无论怎么操作都不可能将任何数字变为0.
不能有奇数个1,观察我们的操作,a[i],a[i+1],a[i+2]同时变为a[i] xor a[i+1] xor a[i+2],如果三个数字中有0个1,则不变,增加了0个1,减少了0个1;如果三个数字中有1个1,则全部变为1,增加了2个1;如果三个数字中有2个1,则数字全都变为0,减少了2个1;如果三个数字全都为1,则不变。可以看出,1只能偶数个增加和偶数个减少,所以奇数个1的情况下无论怎么养也至少会剩下一个1。
结论1:数字全部都是1或者奇数个1的情况下无解
排除前面的情况,也就是数字不全都为1,且1的个数是偶数。
n为奇数的情况下,我们对奇数位置1,3,5,…,n-2都进行一次操作后,可以使得每个奇数位置i,a[i]=a[i+1],也就是a[1]=a[2],a[3]=a[4],a[5]=a[6],…,a[n-2]=a[n-1],并且a[n-2]=a[n-1]=a[n].并且a[n-2]=a[n-1]=a[n]=0。下面证明一下,首先最后三个数字一定相同是显然的,并且1的数量为偶数,不管操作多少次也还会是偶数,如果最后三个数字是1,前面数字两两相等,那么1的数量就是奇数了,反证得到最后3个数字一定是0.最后3个数字一定是0,那么只需要再从n-4,n-6,…,1就能让所有数字都为0了。
结论2:n为偶数,先按照1,3,5…,n-2执行一次操作,再从n-4,n-6,…,1执行一次操作
n为偶数的情况下, 如果最前面是0或者最后面是0我们可以把最前面或者最后面忽略掉看成上面的情况去处理。如果最前面和最后面都是1的时候,就找到一个奇数的位置,前面1的数量是偶数就行了。这个位置开始往前就是奇数个数字偶数个1的情况,按照上面的方法全部弄成0。这个位置后面到末尾也剩下偶数个1,奇数个数字,也按照上面的方法弄成0。如果没有任何一个奇数的位置前面1的个数为偶数,也就是所有奇数的位置前面1的个数都为奇数,首先a[1]=1,第3位之前1个数要是奇数,那么需要a[2]=a[3],往下走同理a[4]=a[5],…,a[n-2]=a[n-1].因为偶数位置上以及后面一个数字两两相同,并且a[1]=1,那么你无论怎么操作,都没办法使得第1位变为0。所以如果找不到一个奇数位置前面1的个数为偶数则无解。
结论3:n为偶数,如果可以找到一个奇数位置p,前面1个数和位偶数,那么(1p),按照结论2操作,(p+1n)按照结论2操作,否则无解
代码
#include<bits/stdc++.h>
using namespace std;
int T;
int a[200005];
int ans[200005];
int tot=0;
int main()
{
scanf("%d",&T);
while(T--)
{
int n;
tot=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int num1=0;//1的数量
int f=1;//1有解0无解
for(int i=1;i<=n;i++)
{
if(a[i]==1)
num1++;
}
if(num1%2==1||num1==n)//结论1:如果1的数量为奇数或者数字全部为1则无解
{
printf("NO\n");
continue;
}
if(n%2)//结论2:n为奇数的情况下
{
for(int i=1;i<=n-2;i+=2)//先按照1,3,5,..,n-2执行操作
{
ans[++tot]=i;
}
for(int i=n-4;i>=1;i-=2)//再按照n-4,n-6,n-8,...,1执行一次操作
{
ans[++tot]=i;
}
}else//结论3:n为偶数的情况下
{
if(a[1]==0)//首是0,忽略第一个数字,就看成了上面n为奇数的情况
{
for(int i=2;i<=n-2;i+=2)
{
ans[++tot]=i;
}
for(int i=n-4;i>=2;i-=2)
{
ans[++tot]=i;
}
}else if(a[n]==0)//末尾是0,忽略最后一个数字,看成n为奇数的情况
{
for(int i=1;i<=n-3;i+=2)
{
ans[++tot]=i;
}
for(int i=n-5;i>=1;i-=2)
{
ans[++tot]=i;
}
}else//首尾都不是0,找到第一个奇数位置,前面1的个数为偶数,找不到则无解
{
int num=0;//当前1的个数
int p=0;//需要找到的奇数位置
for(int i=1;i<=n;i++)
{
if(a[i]==1)
num++;
if(i%2==1&&num%2==0)//找到位置了
{
p=i;
break;
}
}
if(p==0)//没找到p就还是0,则无解
{
f=0;
}
//(1~p)进行结论2操作,(p+1~n)按照结论2操作
for(int i=1;i<=p-2;i+=2)
{
ans[++tot]=i;
}
for(int i=p-4;i>=1;i-=2)
{
ans[++tot]=i;
}
for(int i=p+1;i<=n-2;i+=2)
{
ans[++tot]=i;
}
for(int i=n-4;i>=p+1;i-=2)
{
ans[++tot]=i;
}
}
}
if(!f)
printf("NO\n");
else
{
printf("YES\n");
printf("%d\n",tot);
for(int i=1;i<=tot;i++)
{
printf("%d ",ans[i]);
}
printf("\n");
}
}
return 0;
}