P9990 [ynoi2023] TEST_90 题解
因为把几个数异或起来得到的结果是其中出现次数是奇数的数的异或,所以可以给每个值赋一个随机值,然后求区间内每个出现过的数的个数都为奇数的区间就是求出现的所有数对应的随机值的异或是不是和所有出现的随机值的异或相同,把这两个再异或起来,也就是所有出现为偶数次的数的随机值的异或,那么判断这个东西是不是 0 就可以判断。
考虑什么时候会错误,就是存在一个区间内出现为偶数次的数的随机值的异或是 0。一共有 \(n^2\) 个区间,也就是只存在小于 \(n^2\) 个出现为偶数次的数的组合。考虑从小到大把随机值加入每个含有它的组合,那么每次加入出现组合的异或是零的概率小于 \(\frac{n^2}{v}\),其中 \(v\) 是值域。如果用 unsigned long long ,那么可取 \(v=10^{19},n=10^5\),那么正确概率大于每次加入都没有出现零的概率大于 \((1-\frac{n^2}{v})^n=(1-10^{-9})^{10^5} \approx 1-10^{-4}\)。所以这种方法得到答案正确概率足够大。
然后用题目上面说的:要求一个区间的所有区间,转化为离线后扫描线,记 \(f_i\) 为以 \(i\) 为左端点的所有区间贡献和,那么当扫描到 \(r\) 时,答案即为 \(\sum\limits_{i=l}^r f_i\)
这里贡献是所有出现为偶数次的数的随机值的异或是不是 0,可以设 \(w_i\) 是这个值,那么扫到 \(i\) 只要对于前面所有的 \(w_i\) 异或上 \(i\) 的随机值,然后在 \(i\) 到前面第一个和 \(i\) 相同 \(j\) 的 \(w_i\) 再异或上 \(i\) 的随机值就可以实现,也就相当于 \(1-j\) 异或上 \(i\) 的随机值(所有出现为偶数次的数的随机值的异或是出现的所有数对应的随机值的异或和所有出现的随机值的异或的异或)
这样就只要实现操作1:区间 \(w_i\) 异或一个值,操作2:区间 \(f_i \leftarrow f_i+[w_i=0]\) 操作3:求 \(\sum\limits_{i=l}^{r}f_i\)
用分块实现,其中每个块包含异或标记,整个块加上的数量,整个块含有的数,对于整个块的操作1只要异或这个标记,对于整个块的操作2只要求出其中等于异或标记的数的数量,然后把这个标记记录下来。对于不是整个块的操作需要根据记录下来的标记把整个块重构。如果用哈希表记录整个块含有的数,块长是 \(\sqrt{n}\),那么复杂度是 \(O(n\sqrt{n})\)
注意挂在题目上面的常数优化。
#include<bits/stdc++.h>
using namespace std;
#define long unsigned long long
const long mod=(1<<9)-1;
int id[100010],in[100010],lt[100010],bk,n,m;
long is[100010],t[100010],w[100010],res[100010];
struct ds{int p,x;};vector<ds>c[100010];mt19937_64 rnd(time(0));
struct MAP{
struct sd{long p;int v,next;}e[mod+1];
int hd[mod+1],hv[mod+1],cnt,tp;
int operator [](long x){
for(int i=hd[x&mod];i;i=e[i].next)if(e[i].p==x)return e[i].v;
return 0;
}
void add(long x){
long y=x&mod;
for(int i=hd[y];i;i=e[i].next)if(e[i].p==x){e[i].v++;return;}
if(!hd[y])hv[++tp]=y;e[++cnt]=(sd){x,1,hd[y]},hd[y]=cnt;
}
void clear(){while(tp)hd[hv[tp--]]=0;cnt=0;}
};
struct BK{
MAP cs,ad;long lz,v;int l,r,f;
void mak(int L,int R,long x){
if(L<=l&&r<=R){lz^=x,f=1;return;}
cs.clear();if(f){for(int i=l;i<=r;i++)t[i]+=ad[w[i]],w[i]^=lz;f=lz=0,ad.clear();}
for(int i=max(l,L);i<=min(r,R);i++)w[i]^=x;for(int i=l;i<=r;i++)cs.add(w[i]);
}
void add(int L,int R){
if(L<=l&&r<=R){int x=cs[lz];v+=x;if(x)ad.add(lz),f=1;return;}
if(f){cs.clear();for(int i=l;i<=r;i++)t[i]+=ad[w[i]],cs.add(w[i]^=lz);f=lz=0,ad.clear();}
for(int i=max(l,L);i<=min(r,R);i++)if(w[i]==0)t[i]++,v++;
}
long qry(int L,int R){
if(L<=l&&r<=R)return v;
if(f){cs.clear();for(int i=l;i<=r;i++)t[i]+=ad[w[i]],cs.add(w[i]^=lz);f=lz=0,ad.clear();}
long ret=0;for(int i=max(l,L);i<=min(r,R);i++)ret+=t[i];return ret;
}
}b[500];
void mak(int l,int r,long x){
int L=in[l],R=in[r];
for(int i=L;i<=R;i++)b[i].mak(l,r,x);
}
void add(int l,int r){
int L=in[l],R=in[r];
for(int i=L;i<=R;i++)b[i].add(l,r);
}
long qry(int l,int r){
int L=in[l],R=in[r];long ret=0;
for(int i=L;i<=R;i++)ret+=b[i].qry(l,r);
return ret;
}
int main(){
cin.tie(0)->sync_with_stdio(0);
cin>>n>>m,bk=sqrt(n);
for(int i=1;i<=n;i++)is[i]=rnd(),cin>>id[i];
for(int i=1;b[i-1].r!=n;i++){
b[i].l=b[i-1].r+1,b[i].r=min(n,b[i].l+bk-1);
for(int j=b[i].l;j<=b[i].r;j++)in[j]=i,b[i].cs.add(0);
}
for(int i=1,l,r;i<=m;i++)cin>>l>>r,c[r].push_back((ds){i,l});
for(int i=1;i<=n;i++){
if(lt[id[i]])mak(1,lt[id[i]],is[id[i]]);
add(1,i),lt[id[i]]=i;for(ds x:c[i])res[x.p]=qry(x.x,i);
}
for(int i=1;i<=m;i++)cout<<res[i]<<"\n";
return 0;
}

浙公网安备 33010602011771号