Codeforces Round #743 (Div. 2) D. Xor of 3

题意

给你一个长度为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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值