P8257 题解
Update(26.01.14):复杂度分析有误,已修改,感谢 @流泪的小酒窝 指出。
核桃周赛搬的,已严肃学习 hhoppitree 题解做法,记录一下。
题意
给定序列
题解
询问所有子区间考虑离线,按照右端点排序后扫描,并维护每个点到当前点的种类数
将该贡献放到
注意到
因此现在只需快速维护
考虑从低位到高位对所有
另外需支持迅速找到整块内目前
还需要解决标记下传问题,
分析出来总复杂度为
代码
#include<iostream>
#include<vector>
#define fi first
#define se second
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int N=4e5+10;
const int B=128;
const int M=B+10;
const int K=N/B+10;
int n,m,a[N],res[N],last[N],lp[N],lg[M],tp[M],id[N],L[K],R[K],c[N];
int tag[K],b[2][N],s[2][K],w[K][M],fp[K][M],po[K][M],g[2][M<<1];
bool t[K][2][M],ct[M<<1]; // t 也是标记,记录每个块每种值操作次数的奇偶性
vector <pii> ask[N];
void build(int u,int d,int x)
{
if((1<<d)==B) {tp[x]=u; return;}
lg[u]=d,build(u<<1,d+1,x),build(u<<1|1,d+1,x|(1<<d));
} // 建出 B 位的 Trie,并预处理每种值的位置 tp_x
void built(int id)
{
s[0][id]=s[1][id]=0;
for(int i=0;i<=B;i++)
{
w[id][i]=fp[id][i]=0;
for(int o=0;o<2;o++) t[id][o][i]=0;
} // 清空原信息
for(int i=0;i<(B<<1);i++) ct[i]=0;
for(int i=L[id];i<=R[id];i++)
{
s[0][id]^=b[0][i],s[1][id]^=b[1][i];
ct[tp[c[i]&(B-1)]]^=1,fp[id][B-1-(c[i]&(B-1))]++;
}
for(int i=1;i<=B;i++) fp[id][i]+=fp[id][i-1];
for(int i=L[id];i<=R[id];i++) po[id][--fp[id][B-1-(c[i]&(B-1))]]=i; //类似桶排用 po 按值排序记录所有位置,fp 记录每种值的左端点
for(int i=B-1;i;i--) ct[i]=(ct[i<<1]^ct[i<<1|1]);
g[0][1]=ct[1];
for(int i=2;i<(B<<1);i++)
{
g[0][i]=g[0][i>>1];
if(i<B) g[0][i]|=(ct[i]<<lg[i]);
}
for(int i=0;i<B;i++) w[id][i]=g[0][tp[B-1-(i&(B-1))]]; // 用 Trie 计算贡献数组 w
}
void push(int id)
{
for(int o=0;o<2;o++)
{
for(int i=0;i<(B<<1);i++) ct[i]=0;
for(int i=0;i<B;i++) ct[tp[i]]^=t[id][o][i];
for(int i=B-1;i;i--) ct[i]=(ct[i<<1]^ct[i<<1|1]);
g[o][1]=ct[1];
for(int i=2;i<(B<<1);i++)
{
g[o][i]=g[o][i>>1];
if(i<B) g[o][i]|=(ct[i]<<lg[i]);
}
}
for(int i=L[id];i<=R[id];i++)
{
for(int o=0;o<2;o++) b[o][i]^=g[o][tp[B-1-(c[i]&(B-1))]];
c[i]+=tag[id];
} // 同样用 Trie 进行 c 值的更新
tag[id]=0;
}
void pt(int o,int id)
{
int v=(tag[id]&(B-1)); tag[id]++;
s[o][id]^=w[id][v],t[id][o][v]^=1;
for(int i=fp[id][v];i<fp[id][v+1];i++)
{
int p=po[id][i],d=((c[p]+tag[id])^(c[p]+tag[id]-1)^(B-1));
s[o][id]^=d,b[o][p]^=d;
} // 暴力操作对高位产生影响的位置
} // 操作一个整块
void update(int o,int l,int r)
{
int pl=id[l],pr=id[r];
if(pl==pr)
{
push(pl);
for(int i=l;i<=r;i++) c[i]++,b[o][i]^=c[i]^(c[i]-1);
built(pl);
}
else
{
push(pl),push(pr);
for(int i=l;i<=R[pl];i++) c[i]++,b[o][i]^=c[i]^(c[i]-1);
for(int i=L[pr];i<=r;i++) c[i]++,b[o][i]^=c[i]^(c[i]-1);
for(int i=pl+1;i<pr;i++) pt(o,i);
built(pl),built(pr);
}
}
int query(int o,int l,int r)
{
int tr=0,pl=id[l],pr=id[r];
if(pl==pr)
{
push(pl),built(pl);
for(int i=l;i<=r;i++) tr^=b[o][i];
}
else
{
push(pl),push(pr),built(pl),built(pr);
for(int i=l;i<=R[pl];i++) tr^=b[o][i];
for(int i=L[pr];i<=r;i++) tr^=b[o][i];
for(int i=pl+1;i<pr;i++) tr^=s[o][i];
}
return tr;
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m,build(1,0,0);
for(int i=1;i<=n;i++) cin>>a[i],lp[i]=last[a[i]]+1,last[a[i]]=i,id[i]=(i-1)/B+1,R[id[i]]=i;
for(int i=n;i;i--) L[id[i]]=i;
for(int i=1,l,r;i<=m;i++) cin>>l>>r,ask[r].pb({l,i});
for(int i=1;i<=n;i++)
{
update(i&1,lp[i],i);
for(pii te:ask[i]) res[te.se]=query(i&1,te.fi,i);
}
for(int i=1;i<=m;i++) cout<<res[i]<<'\n';
return 0;
}