原文链接https://www.lydsy.com/JudgeOnline/problem.php?id=2821
题目传送门 - BZOJ2821
题意
$n$ 个数,$m$ 组询问,每次问 $[l,r]$ 中有多少个数出现正偶数次。
$1leq n,m,a_ileq 10^5$
题解
这题的标算是一个分块。但是我不想写分块怎么办?
bitset 大法好!
bitset 大法好!
bitset 大法好!
发现我们可以每隔 $sqrt n$ 保存一下前缀序列中每一个数字的出现情况。
然后询问的时候相当于找到长度 $L-1$ 的前缀序列的数的出现情况和长度 $R$ 的前缀序列的数的出现情况,异或起来,数 0 的个数即可。这个显然可以通过预处理的东西暴力调整,然后手写 bitset 再加一个预处理可以使统计 0 的个数的复杂度也变成 $O(nm/32)$ 。所以总复杂度 $O(nm/32)$ 。这里注意一下调整 $L-1$ 和 $R$ 的 bitset 不能是同一个!。
然后发现样例萎掉了。
发现一个数没有出现过不能算进去。
那怎么办呢?
补集转化一下:答案变成 “区间不同数值的种数 - 统计异或得到的 bitset 的 1 的个数” 。
显然“区间不同数值的种数”是可以通过转化成二维数点问题的,直接拖主席树板子。
然后就 AC 啦。
时间复杂度 $O(nlog n+nsqrt n+n^2/32)$ (由于 $n,m,c$ 同阶,所以这里都看作 $n$ )。
代码
#include <bits/stdc++.h> #define y1 __zzd001 using namespace std; typedef unsigned uint; const int N=100405; int cnt1[65536]; void init(){ for (int i=0;i<65536;i++) cnt1[i]=cnt1[i>>1]+(i&1); } struct BitSet{ uint v[3200]; void clear(){ memset(v,0,sizeof v); } uint XOR(int x){v[x>>5]^=1<<(x&31);} }; int calc(uint v){ return cnt1[v>>16]+cnt1[v&65535]; } struct Ptree{ static const int S=N*40; int n; int root[N],sum[S],ls[S],rs[S],tot; void build(int &rt,int L,int R){ sum[rt=++tot]=0; if (L==R) return; int mid=(L+R)>>1; build(ls[rt],L,mid); build(rs[rt],mid+1,R); } void update(int prt,int &rt,int L,int R,int x){ if (!rt||rt==prt) sum[rt=++tot]=sum[prt]; sum[rt]++; if (L==R) return; if (!ls[rt]) ls[rt]=ls[prt]; if (!rs[rt]) rs[rt]=rs[prt]; int mid=(L+R)>>1; if (x<=mid) update(ls[prt],ls[rt],L,mid,x); else update(rs[prt],rs[rt],mid+1,R,x); } int query(int rt,int L,int R,int xL,int xR){ if (!rt||R<xL||L>xR) return 0; if (xL<=L&&R<=xR) return sum[rt]; int mid=(L+R)>>1; return query(ls[rt],L,mid,xL,xR)+query(rs[rt],mid+1,R,xL,xR); } int Query(int x1,int x2,int y1,int y2){ if (x1>x2||y1>y2) return 0; return query(root[x2],0,n,y1,y2)-query(root[x1-1],0,n,y1,y2); } void init(int _n){ tot=0; n=_n; build(root[0],0,n); } void insert(int x,int y){ update(root[x-1],root[x],0,n,y); } }pt; int read(){ int x=0; char ch=getchar(); while (!isdigit(ch)) ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } int n,m,a[N],Pre[N],c; BitSet v1[330],v2[330]; int main(){ init(); n=read(),c=read(),m=read(); pt.init(n); memset(Pre,0,sizeof Pre); for (int i=1;i<=n;i++){ a[i]=read(); pt.insert(i,Pre[a[i]]); Pre[a[i]]=i; } v1[0].clear(),v2[0]=v1[0]; for (int i=1;i*320<=n;i++){ v1[i]=v1[i-1]; for (int j=(i-1)*320+1;j<=i*320;j++) v1[i].XOR(a[j]); v2[i]=v1[i]; } int ans=0; while (m--){ int L=(read()+ans)%n+1,R=(read()+ans)%n+1; if (L>R) swap(L,R); int all=pt.Query(L,R,0,L-1); L--; int l=L/320,r=R/320; BitSet &sl=v1[l],&sr=v2[r]; l*=320,r*=320; while (l<L) sl.XOR(a[++l]); while (r<R) sr.XOR(a[++r]); int tot=0; for (int i=0;i<3200;i++) tot+=calc(sl.v[i]^sr.v[i]); printf("%d ",ans=all-tot); int ls=L/320*320,rs=R/320*320; while (l>ls) sl.XOR(a[l--]); while (r>rs) sr.XOR(a[r--]); } return 0; }