#贪心#CF1054D Changing Array

题目

给定 \(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);
}
posted @ 2021-12-21 17:23  lemondinosaur  阅读(18)  评论(0编辑  收藏  举报