题解[P7470 岛屿探险]
题意
给出一个序列 \(a_{1\dots n},b_{1\dots n}\),
每次查询给出一段区间 \(l,r\) 以及两个数 \(c,d\),
问区间 \([l,r]\) 内有多少个数满足 \(a_i\operatorname{xor}c\leq \min(b_i,d)\)。
太久没写 \(\text{cdq}\) 竟还能这么快过掉。
首先,由于询问可加性,区间的限制可以拆为两个 \([1,t]\) 的查询。
于是这就可以以位置为第一维进行 \(\text{cdq}\) 分治了。
然后为了方便地拆掉 \(\min(b_i,d)\),可以将 \(\geq d\) 的 \(b_i\) 与 \(< d\) 的,分开统计。
先是 \(b_i\geq d\) 的 \(i\) ,此时有 \(a_i\operatorname{xor}c\leq d\)。这是 \(\text{01trie}\) 的经典运用。将所有 \(a_i\) 插到 \(\text{01trie}\) 上。查询时在 \(\text{01trie}\) 上按着一条 \(c\operatorname{xor}s=d\) 的链 \(s\) ,这使得前几位 \(\operatorname{xor}c\) 后与 \(d\) 相同。如果这一位上 \(d\) 为 \(1\),则累加上使这一位 \(\operatorname{xor}c=0\) 的数的个数,跳到叶子节点的数的个数同样要算上,此时 \(\operatorname{xor}c=d\)。
而对于 \(b_i<d\) 的 \(i\),有 \(a_i\operatorname{xor}c\leq b_i\)。考虑将能使 \(a_i\operatorname{xor}c\leq b_i\) 的 \(c\) 处打上 \(+1\) 的标记。维护一条使 \(s\operatorname{xor}a_i=b_i\) 的链 \(s\),如果这一位上 \(b_i\) 为 \(1\),将 \(\operatorname{xor}a_i\) 后这一位为 \(0\) 的子树打上 \(+1\) 的标记,跳到的叶子节点同样要打上 \(+1\) 的标记。查询时按着 \(c\) 从根开始一路累加被加上的标记和即可。
时间复杂度为 \(O(n\log n\log V)\),\(\text{cdq}\) 与 \(\text{01trie}\) 各带一个 \(\log\)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10,K=24;
int n,m,x,y,l,r,a,b,tot,t_tot,rt,tmp;
int res[N];bool bx,bw;char ch;
inline void read(int &x){
x=0;ch=getchar();while(ch<48)ch=getchar();
while(ch>47)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}
void write(int x){if(x>9)write(x/10);putchar(48+x%10);}
struct cdq{
int i,a,b,id;char opt;cdq()=default;//opt=0:mdf otherwise:inq
cdq(int _i,int _a,int _b,char _opt=0,int _id=0)
:i(_i),a(_a),b(_b),opt(_opt),id(_id){}
}q[N],q0[N],qq[N],qq0[N];//min=d for q ; min=b for qq
bool cmpi(cdq x,cdq y){return x.i^y.i?x.i<y.i:!x.opt;}
bool cmpq(cdq x,cdq y){return x.b^y.b?x.b>y.b:!x.opt;}
bool cmpqq(cdq x,cdq y){return x.b^y.b?x.b<y.b:x.opt;}
struct trie01{int son[2],tag[2],sz;}t[N*(K+1)];
void update(int &k,int x,int i,int v){
if(!k)k=++t_tot;t[k].sz+=v;
if(~i)update(t[k].son[(x>>i)&1],x,i-1,v);
}
void update(int &k,int x,int w,int i,int v){
if(!k)k=++t_tot;
bw=(w>>i)&1;bx=bw^((x>>i)&1);
if(~i){
if(bw)t[k].tag[!bx]+=v;
update(t[k].son[bx],x,w,i-1,v);
}
else t[k].tag[0]+=v;
}
void inquiry(int k,int x,int w,int i){
if(~i){
bw=(w>>i)&1;bx=bw^((x>>i)&1);
if(bw)tmp+=t[t[k].son[!bx]].sz;
inquiry(t[k].son[bx],x,w,i-1);
}
else tmp+=t[k].sz;
}
void inquiry(int k,int x,int i){
if(~i){
bx=(x>>i)&1;tmp+=t[k].tag[bx];
inquiry(t[k].son[bx],x,i-1);
}
else tmp+=t[k].tag[0];
}
void solve(int l,int r){
if(l^r){
int mid=(l+r)>>1,i;
solve(l,mid);solve(mid+1,r);
merge(q+l,q+mid+1,q+mid+1,q+r+1,q0+l,cmpq);
merge(qq+l,qq+mid+1,qq+mid+1,qq+r+1,qq0+l,cmpqq);
for(i=l;i<=r;++i)q[i]=q0[i],qq[i]=qq0[i];
for(i=l;i<=r;++i){
if(q[i].i<=mid&&!q[i].opt)update(rt,q[i].a,K,1);
else if(q[i].i>mid&&q[i].opt){
tmp=0;inquiry(rt,q[i].a,q[i].b,K);
res[q[i].id]+=tmp*q[i].opt;
}
}
for(i=l;i<=r;++i)if(q[i].i<=mid&&!q[i].opt)update(rt,q[i].a,K,-1);
for(i=l;i<=r;++i){
if(qq[i].i<=mid&&!qq[i].opt)update(rt,qq[i].a,qq[i].b,K,1);
else if(qq[i].i>mid&&qq[i].opt){
tmp=0;inquiry(rt,qq[i].a,K);
res[qq[i].id]+=tmp*qq[i].opt;
}
}
for(i=l;i<=r;++i)if(qq[i].i<=mid&&!qq[i].opt)update(rt,qq[i].a,qq[i].b,K,-1);
}
}
main(){
read(n);read(m);register int i;
for(i=1;i<=n;++i)read(a),read(b),q[++tot]=cdq(i,a,b);
for(i=1;i<=m;++i){
read(l),read(r),read(a),read(b);
q[++tot]=cdq(r,a,b,1,i);
if(l>1)q[++tot]=cdq(l-1,a,b,-1,i);
}
sort(q+1,q+tot+1,cmpi);
for(i=1;i<=tot;++i)q[i].i=i;
for(i=1;i<=tot;++i)qq[i]=q[i];
solve(1,tot);
for(i=1;i<=m;++i)write(res[i]),putchar('\n');
}