P7470-[NOI Online 2021 提高组]岛屿探险【Trie,CDQ分治】
正题
题目链接:https://www.luogu.com.cn/problem/P7470
题目大意
给出\(n\)个二元组\((a,b)\)。
\(q\)次询问给出\((l,r,c,d)\)表示询问\([l,r]\)中有多少二元组满足\(c\ xor\ a\leq min(b,d)\)。
\(1\leq n,q\leq 10^5\)
解题思路
这个\(min\)一看就很迷,显然是让我们分两种情况讨论。
再把询问拆一下,就变成了两个条件\(pos\leq r/pos<l\)且\(b\leq d/b>d\)。
两个偏序条件的话直接上\(CDQ\),然后考虑两种情况怎么处理。
- \(c\ xor\ a\leq b\):这样对于每个二元组合法的\(c\)开业被拆成\(Trie\)上最多\(log\)个区间,建\(Trie\)即可
- \(c\ xor\ a\leq d\):对于每组询问在\(Trie\)上跑区间求和即可。
时间复杂度\(O(n\log^2 n)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1e5+10,M=N*24;
struct node{
int w,l,id;
}q[N<<1],a[N];
int n,m,tot,rt1,rt2,ans[N];
vector<node> v[N];
struct Trie1{
int cnt,ch[M][2],w[M];
void Clear(){rt1=0;cnt=0;return;}
int Newp(){++cnt;ch[cnt][0]=ch[cnt][1]=w[cnt]=0;return cnt;}
void Insert(int &x,int d,int l,int val){
if(!x)x=Newp();
if(d<0){w[x]++;return;}
int c=(val>>d)&1;
if((l>>d)&1){
Insert(ch[x][c^1],d-1,l,val);
if(!ch[x][c])ch[x][c]=Newp();
w[ch[x][c]]++;
}
else Insert(ch[x][c],d-1,l,val);
}
int Ask(int x,int d,int val){
if(!x)return 0;
if(d<0)
return w[x];
int c=(val>>d)&1;
return Ask(ch[x][c],d-1,val)+w[x];
}
}T1;
struct Trie2{
int cnt,ch[M][2],w[M];
void Clear(){rt2=0;cnt=0;return;}
int Newp(){++cnt;ch[cnt][0]=ch[cnt][1]=w[cnt]=0;return cnt;}
void Insert(int &x,int d,int val){
if(!x)x=Newp();
if(d<0){w[x]++;return;}
int c=(val>>d)&1;
Insert(ch[x][c],d-1,val);
w[x]=w[ch[x][0]]+w[ch[x][1]];
return;
}
int Ask(int x,int d,int l,int val){
if(d<0)return w[x];
int c=(val>>d)&1;
if((l>>d)&1)
return Ask(ch[x][c^1],d-1,l,val)+w[ch[x][c]];
return Ask(ch[x][c],d-1,l,val);
}
}T2;
bool cmp(node x,node y)
{return x.l<y.l;}
void CDQ(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
CDQ(l,mid);
CDQ(mid+1,r);
sort(a+l,a+mid+1,cmp);
T1.Clear();T2.Clear();tot=0;
for(int i=mid+1;i<=r;i++)
for(int j=0;j<v[i].size();j++)
q[++tot]=v[i][j];
sort(q+1,q+1+tot,cmp);
for(int i=1,z=l;i<=tot;i++){
while(z<=mid&&a[z].l<=q[i].l)
T1.Insert(rt1,23,a[z].l,a[z].w),z++;
if(q[i].id<0)ans[-q[i].id]-=T1.Ask(rt1,23,q[i].w);
else ans[q[i].id]+=T1.Ask(rt1,23,q[i].w);
}
for(int i=tot,z=mid;i>=1;i--){
while(z>=l&&a[z].l>q[i].l)
T2.Insert(rt2,23,a[z].w),z--;
if(q[i].id<0)ans[-q[i].id]-=T2.Ask(rt2,23,q[i].l,q[i].w);
else ans[q[i].id]+=T2.Ask(rt2,23,q[i].l,q[i].w);
}
return;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i].w,&a[i].l);
for(int i=1;i<=m;i++){
int l,r,c,d;
scanf("%d%d%d%d",&l,&r,&c,&d);
v[l].push_back((node){c,d,-i});
v[r+1].push_back((node){c,d,i});
}
sort(q+1,q+1+n,cmp);
CDQ(1,n+1);
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}