题目
给定 \(n\) 个 \(k\) 位二进制数,\(n\leq 2*10^5,k\leq 30\)
可以选择若干数将其所有二进制位取反,
最多可以有多少个区间的异或和不为 0
分析
考虑将区间异或和改成前缀异或和的异或,
那么每次就是让与前面重复的异或值个数尽量小,
可以用哈希维护
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define rr register
using namespace std;
typedef long long lll;
const int N=100011,p=600011;
int n,a[N],b[N],al,sum; lll ans;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
struct Linked_Hash{
struct node{int y,w,next;}E[p]; int Et,hs[p];
inline void Clear(){Et=0,memset(hs,-1,sizeof(hs));}
inline void Insert(int w,int x){E[++Et]=(node){x,w,hs[w%p]},hs[w%p]=Et;}
inline signed locate(int W){
for (rr int i=hs[W%p];~i;i=E[i].next)
if (E[i].w==W) return i;
return -1;
}
}H;
signed main(){
n=iut(),H.Clear(),H.Insert(0,1),
al=(1<<iut())-1,ans=(lll)n*(n+1)/2;
for (rr int i=1;i<=n;++i){
rr int s1=sum^iut(),s2=s1^al;
rr int pos1=H.locate(s1),pos2=H.locate(s2);
if (pos1==-1) H.Insert(s1,0),pos1=H.Et;
if (pos2==-1) H.Insert(s2,0),pos2=H.Et;
if (H.E[pos1].y<H.E[pos2].y) ans-=H.E[pos1].y++,sum=s1;
else ans-=H.E[pos2].y++,sum=s2;//取反与不取反选小的那一个
}
return !printf("%lld",ans);
}