CF1572B Xor of 3 题解
解题思路
首先我们知道,当序列中有奇数个 NO
即可。
不难发现,序列的异或和为一定值,证明如下:
- 令
表示每一次操作选取的三个数的异或和,令 表示整个序列的异或和; - 每次操作可以看作删去三个数、添加三个数,不妨设这三个数分别为
,那么在删去三个数的时候, ,替换一下可得 ,新添入三个 后 ,打开括号后可得 ,异或和不变。
显然直接从左往右扫一次是可以更新形如 011110
和 10111
的段的,但是序列中显然还可能存在形如 01000010
的段,这种情况下直接扫描是无法更新的。我们考虑将后面的段转化为前面的段。由于每次操作最多会产生
在全部转化后,再从左往右扫一次就可以得到答案了。
总共需要
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();}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下