Title

CF1572B Xor of 3 题解

解题思路

首先我们知道,当序列中有奇数个 1 的时候无解,因为每次操作只会增加或减少 201,当序列中有奇数个 1 的时候,一定会存在一个 1 无法被消除。因此,在这种情况下,直接输出 NO 即可。

不难发现,序列的异或和为一定值,证明如下:

  • sx 表示每一次操作选取的三个数的异或和,令 s 表示整个序列的异或和;
  • 每次操作可以看作删去三个数、添加三个数,不妨设这三个数分别为 x1,x2,x3,那么在删去三个数的时候,ss(x1x2x3),替换一下可得 sssx,新添入三个 sxs(ssx)sxsxsx,打开括号后可得 ss,异或和不变。

显然直接从左往右扫一次是可以更新形如 01111010111 的段的,但是序列中显然还可能存在形如 01000010 的段,这种情况下直接扫描是无法更新的。我们考虑将后面的段转化为前面的段。由于每次操作最多会产生 2 的影响,我们可以每次枚举 i=2k+1,kN 的位置,这样对于最终的答案是没有影响的。由于序列前面某一段可能需要倒序更新,而后面的段只需要直接暴力修改,那么我们考虑找到第一段需要倒序修改的段,由于从 1 开始的一段连续的 0 不管是否修改都没有影响,那么一段需要修改的段一定不能只包括 0。同理,由一段中需要有偶数个 1 才能被消除,那么第一段中也应该包含偶数个 1。由此,我们需要找到第一段包含偶数个 1 的且右端为奇数的段,直接枚举即可。

在全部转化后,再从左往右扫一次就可以得到答案了。

总共需要 O(n) 次操作,可以通过本题。

AC 代码

#include<vector>
#include<stdio.h>
#include<stdlib.h>
#define N 200005
int n,a[N],sum[N];
inline void work(){
    scanf("%d",&n);std::vector<int> ans;int _1=0;
    for(register int i=1;i<=n;++i) scanf("%d",&a[i]);
    for(register int i=1;i<=n;++i) if(a[i]==1) ++_1;
    if(_1&1){puts("NO");return;}
    for(register int i=1;i<=n;++i) sum[i]=sum[i-1]+a[i];
    int pos=0;for(register int i=1;i<=n;++i)
        if((i&1)&&sum[i]%2==0){pos=i;break;}
    if(!pos){puts("NO");return;}
    for(register int i=pos-2;i>=1;i-=2) ans.push_back(i);
    for(register int i=pos+1;i<=n-2;i+=2) ans.push_back(i);
    for(register int i=1;i<=n-2;i+=2) ans.push_back(i);
    puts("YES"); printf("%d\n",ans.size()); if(ans.empty()) return;
    for(auto now:ans) printf("%d ",now); putchar('\n');
}signed main(){int T;scanf("%d",&T);while(T--) work();}
posted @   UncleSam_Died  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示