偏序问题

P10814 【模板】离线二维数点

询问区间 \([l,r]\) 内,在值域为 \([0,x]\) 中数的个数。
想象这是个二维平面,我们要求的就是矩形内点的个数。

image

通常用 cdq 做,将询问拆成两个点,将点按照x坐标排序,按照y坐标加入到树状数组中(为了满足第二条限制),这时就依次加入点,并用树状数组进行前缀和相减,用两个前缀求区间信息,并且两个前缀都是在满足限制的情况下计算的。

P2163 [SHOI2007] 园丁的烦恼

同样二维数点题目,不过查询区间改为了矩形,我们同样用二维前缀和的做法,容斥掉多余的部分。

点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define int ll
#define ls p<<1
#define rs p<<1|1 
#define re register 
#define pb push_back
#define pir pair<int,int>
#define f(a,x,i) for(int i=a;i<=x;i++)
#define fr(a,x,i) for(int i=a;i>=x;i--)
#define lb(x) x&(-x); 
using namespace std;
const int N=5e5+10;
const int B=1e6+5;
const int M=8e6+10;
const int mod=1e9+7;

int n,m;

struct ss{
	int x,y,id,op,w;
}a[M];//点坐标

struct sss{
	int x,y,z,h;
}q[N];


int ans[N];//答案

int cnt=1;

int b[M],tot=0;//离散化
int len=0;

int xx[N],yy[N];

bool cmp(ss g,ss h){
	if(g.x!=h.x) return g.x<h.x;
	if(g.y!=h.y) return g.y<h.y;
	return g.w>h.w;
}

int t[N+10];
void add(int x,int k){
	while(x<=len+10){
		t[x]+=k,x+=lb(x);
	}
}
int query(int x){
	int sum=0;
	while(x){
		sum+=t[x],x-=lb(x);
	}
	return sum;
}	
void solve(){
	// freopen("a.in","r",stdin);
	// freopen("a.out","w",stdout);  
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		int x,y;
		cin>>x>>y;
		xx[i]=x;yy[i]=y;
		b[++tot]=x;b[++tot]=y;
	}

	for(int i=1;i<=m;i++){
		int x,y,z,h;
		cin>>x>>y>>z>>h;
		q[i]={x,y,z,h};
		b[++tot]=x;b[++tot]=y;b[++tot]=z;b[++tot]=h;
	}
	
	sort(b+1,b+1+tot);
	len=unique(b+1,b+1+tot)-b-1;

	for(int i=1;i<=n;i++){
		int x,y;
		x=lower_bound(b+1,b+1+len,xx[i])-b+2;
		y=lower_bound(b+1,b+1+len,yy[i])-b+2;
		a[cnt++]={x,y,0,1,1};
	}

	for(int i=1;i<=m;i++){
		int x,y,z,h;
		x=lower_bound(b+1,b+1+len,q[i].x)-b+2;
		y=lower_bound(b+1,b+1+len,q[i].y)-b+2;
		z=lower_bound(b+1,b+1+len,q[i].z)-b+2;
		h=lower_bound(b+1,b+1+len,q[i].h)-b+2;
		a[cnt++]={x-1,y-1,i,1,0};
		a[cnt++]={x-1,h,i,-1,0};
		a[cnt++]={z,y-1,i,-1,0};
		a[cnt++]={z,h,i,1,0};
	}
	cnt--;
	sort(a+1,a+cnt+1,cmp);

	for(int i=1;i<=cnt;i++){
		add(a[i].y,a[i].w);
		if(a[i].w==0){
			int sum=a[i].op*query(a[i].y);
			ans[a[i].id]+=sum;	
		}
	}

	for(int i=1;i<=m;i++){
		cout<<ans[i]<<"\n";
	}
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(nullptr);
	int t=1;
	// cin>>t;
	while(t--){
		solve();
	}
    return 0;
}
P3755 [CQOI2017] 老C的任务

上一题的扩展,每个点带权值,改改就行。

P4390 [BalkanOI2007] Mokia 摩基亚

多了个修改操作,但不是强制在线,这题同时限制了修改时间、纵坐标、横坐标,是个三维偏序问题,cdq分治解决了,我们只要按操作依次加点就是已经满足了时间有序。

点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define int ll
#define ls p<<1
#define rs p<<1|1 
#define re register 
#define pb push_back
#define pir pair<int,int>
#define f(a,x,i) for(int i=a;i<=x;i++)
#define fr(a,x,i) for(int i=a;i>=x;i--)
#define lb(x) x&(-x); 
using namespace std;
const int N=2e6+10;
const int B=1e6+5;
const int M=8e6+10;
const int mod=1e9+7;

int n,m;

struct ss{
	int x,y,id,op,w;
}a[M];//点坐标

struct sss{
	int x,y,z,h;
}q[N];


int ans[N];//答案

int cnt=1;

bool cmp(ss g,ss h){
	if(g.x!=h.x) return g.x<h.x;
	if(g.y!=h.y) return g.y<h.y;
	return g.w>h.w;
}

int t[N+10];
void add(int x,int k){
	while(x<=2e6+10){
		t[x]+=k,x+=lb(x);
	}
}
int query(int x){
	int sum=0;
	while(x){
		sum+=t[x],x-=lb(x);
	}
	return sum;
}	

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);
	sort(a+mid+1,a+r+1,cmp);	
	int i=l,j=mid+1;
	for(;j<=r;j++){
		while(i<=mid&&a[i].x<=a[j].x){
			if(a[i].w!=0){
				add(a[i].y,a[i].w);
			}
			i++;
		}
		if(a[j].w==0){
			ans[a[j].id]+=a[j].op*query(a[j].y);
		}
	}
	for(j=l;j<i;j++){
		if(a[j].w!=0){
			add(a[j].y,-a[j].w);
		}
	}
}

void solve(){
	// freopen("a.in","r",stdin);
	// freopen("a.out","w",stdout);  
	cin>>m>>n;
	m=0,n=0;
	int op=0;
	cin>>op;
	while(op!=3){
		if(op==1){
			int x,y,w;
			cin>>x>>y>>w;
			a[cnt++]={x,y,0,1,w};
		}
		else{
			int x,y,z,h;
			cin>>x>>y>>z>>h;
			m++;
			a[cnt++]={x-1,y-1,m,1,0};
			a[cnt++]={x-1,h,m,-1,0};
			a[cnt++]={z,y-1,m,-1,0};
			a[cnt++]={z,h,m,1,0};
		}
		cin>>op;
	}
	cnt--;
	cdq(1,cnt);

	for(int i=1;i<=m;i++){
		cout<<ans[i]<<"\n";
	}
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(nullptr);
	int t=1;
	// cin>>t;
	while(t--){
		solve();
	}
    return 0;
}

P3157 [CQOI2011] 动态逆序对

单独算贡献,删掉一个数会影响这个数之前比它大的数和之后比它小的数,cdq离线单独求出每次操作所影响的贡献。

#include <bits/stdc++.h>
#define ll long long
#define int ll
#define ls p<<1
#define rs p<<1|1 
#define re register 
#define pb push_back
#define pir pair<int,int>
#define f(a,x,i) for(int i=a;i<=x;i++)
#define fr(a,x,i) for(int i=a;i>=x;i--)
#define lb(x) x&(-x); 
using namespace std;
const int N=1e6+10;
const int B=1e6+5;
const int M=8e6+10;
const int mod=1e9+7;

int n,m;
struct ss{
	int x,id,op,pos,tim;
}a[M];
map<int,int> mp;

int ans[N];//答案
int cnt;

bool cmp(ss g,ss h){
	return g.pos<h.pos;
}
int t[N+10];
void add(int x,int k){
	while(x<=1e6+10){
		t[x]+=k,x+=lb(x);
	}
}
int query(int x){
	int sum=0;
	while(x){
		sum+=t[x],x-=lb(x);
	}
	return sum;
}	

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);
	sort(a+mid+1,a+r+1,cmp);	
	int i=l,j=mid+1;
	for(;j<=r;j++){
		while(i<=mid&&a[i].pos<=a[j].pos){
			add(a[i].x,a[i].op);
			i++;
		}
		ans[a[j].id]+=a[j].op*(query(n)-query(a[j].x));
	}
	for(j=l;j<i;j++){
		add(a[j].x,-a[j].op);
	}
	j=mid;
	for(i=r;i>mid;i--){
		while(j>=l&&a[j].pos>=a[i].pos){
			add(a[j].x,a[j].op);
			j--;
		}
		ans[a[i].id]+=a[i].op*query(a[i].x-1);
	}
	for(i=mid;i>j;i--){
		add(a[i].x,-a[i].op);
	}
}

void solve(){
	// freopen("a.in","r",stdin);
	// freopen("a.out","w",stdout);  
	cin>>n>>m;

	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		mp[x]=i;
		a[++cnt]={x,0,1,i,cnt};
	}
	for(int i=1;i<=m;i++){
		int x;
		cin>>x;
		a[++cnt]={x,i,-1,mp[x],cnt};
	}
	cdq(1,cnt);

	for (int i=1;i<=m;++i){
		ans[i]+=ans[i-1];
	}
    for(int i=0;i<m;i++){
		cout<<ans[i]<<"\n";
	}
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(nullptr);
	int t=1;
	// cin>>t;
	while(t--){
		solve();
	}
    return 0;
}
posted @ 2025-01-24 12:41  sad_lin  阅读(19)  评论(0)    收藏  举报