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;
}
posted @ 2021-09-01 12:40  QuantAsk  阅读(49)  评论(0编辑  收藏  举报