1. 程式人生 > 其它 >P7470-[NOI Online 2021 提高組]島嶼探險【Trie,CDQ分治】

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;
}