场外选手来水波题解。
Part.1
先考虑询问怎么做。
首先我们考虑异或有一个很好的性质,可差分性。
考虑对序列做一个前缀异或和,一段区间$[l,r]$的异或和为$0$,实际上就是$sum[r]\oplus sum[l-1]=0$。所以现在我们只用查询序列中有几个相等的数对,这个就可以随便做了。
Part.2
考虑修改操作很有特点,每次修改的数都是相邻的两个数,对前缀和的影响是$sum[p]$变为了$sum[p]\oplus x$,$sum[p+1...n]$变为了$sum[p+1...n]\oplus x\oplus x=sum[p+1...n]$,实际上发生改变的前缀就只有$sum[p]$!
所以问题就变得很简单了,我们只需要先扫一遍统计一下开始的答案,每次先删除原来$sum[p]$的贡献,再加入修改后的$sum[p]$就可以了。
随便拿个值域数据结构做就是了,我写了个$01\text{trie}$,当然拿动态开点值域线段树或平衡树,$\text{map}$之类的都可以啦。
答案记得开$\text{long long}$。
代码:
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn=1e6+5,lim=29;
int tr[maxn*lim][2],siz[maxn*lim],sum[maxn],cnt=1,n,m,cntx;
ll totans,mx,mi;
template<typename T>
inline void read(T &x)
{
int f=1;char c;
while(!isdigit(c=getchar())) if(c=='-') f=-1;
x=c^48;
while(isdigit(c=getchar())) x=x*10+(c^48);
x*=f;
}
void insert(int x,int f=1)
{
int u=1;
for(int i=lim;~i;--i)
{
int c=x>>i&1;
if(!tr[u][c]) tr[u][c]=++cnt;
u=tr[u][c];siz[u]+=f;
}
}
int query(int x)
{
int u=1;
for(int i=lim;~i;--i)
{
int c=x>>i&1;
u=tr[u][c];
if(!u) return 0;
}
return siz[u];
}
int main()
{
int p,x;
ll ans=0;
read(n);read(m);
for(int i=1;i<=n;++i)
read(sum[i]),sum[i]^=sum[i-1];
for(int i=0;i<=n;++i)
ans+=query(sum[i]),insert(sum[i]);
mx=0;mi=1e18;
while(m--)
{
read(p);read(x);
insert(sum[p],-1);
ans-=query(sum[p]);
sum[p]^=x;
ans+=query(sum[p]);
insert(sum[p]);
totans^=ans;cntx+=(ans&1);
mx=max(mx,ans);mi=min(mi,ans);
}
printf("%lld\n%d\n%lld\n%lld\n",totans,cntx,mx,mi);
return 0,QAQ;
}