[OI] 偏序

\(n\) 维偏序即给出若干个点对 \((a_{i},b_{i},\cdots,n_{i})\),对每个 \(i\) 求出满足 \(a_{j}\gt a_{i},b_{j}\gt b_{i}\cdots,n_{j}\gt n_{i}\)\(j\) 的个数

一维偏序

直接用权值线段树或者树状数组. 或者你直接离散化开桶前缀和.

二维偏序

考虑到先对全部点对按 \(a_{i}\) 排序,这样,任何点对只能被它前面的点产生贡献.

a 1 1 1 2 3 4 4 5
b 1 2 3 3 4 2 4 1

可以发现,\(i\) 处的答案可以转化为:其前方的点中满足一维偏序的点数.

所以我们考虑对点对从左到右扫一遍,每次向维护的动态树状数组中加入一个新节点,统计前缀和即可.

三维偏序

CDQ+数据结构 解法

按二维偏序的套路,先对 \(a_{i}\) 排序

然后我们考虑这样分解问题:向下对 \(b\) 进行归并排序,每次只统计 \(i\in[l,mid],j\in[mid+1,r]\) 的答案. 可以发现,对于区间内部的答案,总会有递归下去的子区间来求解,对于区间之间的答案,当前这一步将会求解出来,因此我们恰好统计了所有答案,这就是 CDQ 分治.

为什么我们在进行统计答案的时候需要对 \(b\) 排序?假设现在两个子区间内的 \(b\) 都是有序的(\(a\) 是完全有序的,我们不管它),那么在对 \(b\) 归并排序的过程中,会发现,只有 \(i\) 能对 \(j\) 产生贡献(因为 \(a\) 数组的限制),并且只有 \(i\) 数对的 \(b\)\(j\) 数对的 \(b\) 更小的时候,\(i\) 才可能对 \(j\) 有贡献,表现在归并排序里就是只有比 \(j\) 先选的 \(i\) 才能对 \(j\) 有贡献. 基于这一点,我们才能用树状数组+CDQ分治来解决偏序问题.

#include<bits/stdc++.h>
using namespace std;
#define p &
int n,k;
struct node{
	int a,b,c;
	int cnt,res;
	bool operator !=(const node &A)const{
		return a!=A.a or b!=A.b or c!=A.c;
	}
};
node e[100001];
node ue[100001];
int m,t;
int res[100001];
struct bit{
	int a[200001];
	function<int(int)> lowbit=[](int x){return x&-x;};
	inline void add(int pos,int val){
		while(pos<=k){
			a[pos]+=val;
			pos+=lowbit(pos);
		}
	};
	inline int ask(int pos){//sum[pos]
		int res=0;
		while(pos){
			res+=a[pos];
			pos-=lowbit(pos);
		}
		return res;
	};
}BIT;
bool cmp1(node a,node b){
	if(a.a!=b.a) return a.a<b.a;
	if(a.b!=b.b) return a.b<b.b;
	return a.c<b.c;
}
bool cmp2(node a,node b){
	if(a.b!=b.b) return a.b<b.b;
	return a.c<b.c;
}
void cdq(int l,int r){
	if(l==r) return;//i!=j 单个区间无法产生贡献 
	int mid=(l+r)/2;
	cdq(l,mid);//递归求解子区间 
	cdq(mid+1,r);
	sort(ue+l,ue+mid+1,cmp2);//按 b 排序,方便归并 
	sort(ue+mid+1,ue+r+1,cmp2);
	int i=l,j=mid+1;//归并排序 
	//注意这里并不会真的排序,因为父区间会有 sort 的 
	while(j<=r){
		while(i<=mid and ue[i].b<=ue[j].b){
			//当前 i 更小,将会对 j 与 j 后方的位置产生贡献 
			BIT.add(ue[i].c,ue[i].cnt);
			i++;
		}
		//后面的 i 值不再能够产生贡献了,此时暂时统计答案 
		ue[j].res+=BIT.ask(ue[j].c);
		j++;
	}
	for(int k=l;k<=i-1;++k){
		BIT.add(ue[k].c,-ue[k].cnt);
		//清空 BIT,memset 也行就是有点慢 
	}
}
int main(){
	scanf("%d %d",p n,p k);
	for(int i=1;i<=n;++i){
		scanf("%d %d %d",p e[i].a,p e[i].b,p e[i].c);
	}
	sort(e+1,e+n+1,cmp1);
	for(int i=1;i<=n;++i){
		t++;
		if(e[i]!=e[i+1]){
			//去重,防止相同的元素统计不到而漏答案 
			ue[++m]={e[i].a,e[i].b,e[i].c,t};
			t=0;
		}
	}
	cdq(1,m);
	for(int i=1;i<=m;++i){
		//去重在这里用到了 
		res[ue[i].res+ue[i].cnt-1]+=ue[i].cnt;
	}
	for(int i=0;i<=n-1;++i){
		printf("%d\n",res[i]);
	}
}

数据结构+数据结构

众所周知,三维偏序可以用树套树来解决.

我并不是非常喜欢写树套树,因此就写了一个树套树

我说怎么没人线段树套线段树,原来是会 TLE 啊,哈哈哈😅

考虑到这个题排完序只需要对每一维维护动态前缀和即可,可以想到树套树.

UPD:糙拟妈啥比树套树爱谁调谁调😠😠😠😠😠😠😠😠😠😠😠😠

10pts

#include<bits/stdc++.h>
using namespace std;
int n,k;
struct node{
	int val;
	node *tol,*tor;
	node(){val=0;tol=tor=NULL;}
};
class Stree{
	public:
	node *root;
	void change(node *&now,int pos,int val,int l,int r){
		if(!now) now=new node();
		if(l==r){
			now->val+=val;
			return;
		}
		int mid=(l+r)/2;
		if(pos<=mid) change(now->tol,pos,val,l,mid);
		else change(now->tor,pos,val,mid+1,r);
		now->val=(now->tol?now->tol->val:0)+(now->tor?now->tor->val:0);
	}
	int ask(node *now,int l,int r,int nl,int nr){
		if(!now) return 0;
		if(l==nl and r==nr) return now->val;
		int mid=(nl+nr)/2;
		if(r<=mid) return ask(now->tol,l,r,nl,mid);
		else if(l>mid) return ask(now->tor,l,r,mid+1,nr);
		return ask(now->tol,l,mid,nl,mid)|ask(now->tor,mid+1,r,mid+1,nr);
	}
};
//struct node{
//	int val;
//	int tol,tor;
//};
//vector<node>t;
//int newnode(){
//	t.push_back({0,0,0});
//	return t.size()-1;
//}
//class Stree{
//	public:
//	int root=0;
//	void change(int &now,int pos,int val,int l,int r){
//		if(!now) now=newnode();
//		if(l==r){
//			t[now].val+=val;
//			return;
//		}
//		int mid=(l+r)/2;
//		if(pos<=mid) change(t[now].tol,pos,val,l,mid);
//		else change(t[now].tor,pos,val,mid+1,r);
//		t[now].val=t[t[now].tol].val+t[t[now].tor].val;
//	}
//	int ask(int now,int l,int r,int nl,int nr){
//		if(!now) return 0;
//		if(l==nl and r==nr) return t[now].val;
//		int mid=(nl+nr)/2;
//		if(r<=mid) return ask(t[now].tol,l,r,nl,mid);
//		else if(l>mid) return ask(t[now].tor,l,r,mid+1,nr);
//		return ask(t[now].tol,l,mid,nl,mid)+ask(t[now].tor,mid+1,r,mid+1,nr);
//	}
//};
class BIT{
	public:
	Stree a[200001];
	inline int lowbit(int x){return x&-x;}
	inline void add(int posx,int posy,int val){
		for(int i=posx;i<=k;i+=lowbit(i)){
			a[i].change(a[i].root,posy,val,1,k);
		}
	}
	inline int ask(int x,int y){
		int ans=0;
		for(int i=x;i;i-=lowbit(i)){
			ans+=a[i].ask(a[i].root,1,y,1,k);
		}
		return ans;
	}
}bit;
struct un{
	int x,y,z;
	bool operator <(const un &A)const{
		if(x!=A.x) return x<A.x;
		if(y!=A.y) return y<A.y;
		return z<A.z;
	}
	bool operator ==(const un &A)const{
		return x==A.x and y==A.y and z==A.z;
	}
}d[100001];
int ans[100001];
int main(){
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;++i){
		scanf("%d %d %d",&d[i].x,&d[i].y,&d[i].z);
	}
	sort(d+1,d+n+1);
	int t=1;
	for(int i=1;i<=n;++i){
		if(d[i+1]==d[i]){
			t++;
			continue;
		}
		bit.add(d[i].y,d[i].z,t);
		int res=bit.ask(d[i].y,d[i].z);
		ans[res]+=t;
		t=1;
	}
	for(int i=1;i<=n;++i){
		printf("%d\n",ans[i]);
	}
}

UPD: 码的调出来了. 哪个啥比把 | 设计在 + 旁边了😠

ask() 函数最后一行把 + 写成 |

#include<bits/stdc++.h>
using namespace std;
int n,k;
struct node{
	int val;
	node *tol,*tor;
	node(){val=0;tol=tor=NULL;}
};
class Stree{
	public:
	node *root;
	void change(node *&now,int pos,int val,int l,int r){
		if(!now) now=new node();
		if(l==r){
			now->val+=val;
			return;
		}
		int mid=(l+r)/2;
		if(pos<=mid) change(now->tol,pos,val,l,mid);
		else change(now->tor,pos,val,mid+1,r);
		now->val=(now->tol?now->tol->val:0)+(now->tor?now->tor->val:0);
	}
	int ask(node *now,int l,int r,int nl,int nr){
		if(!now) return 0;
		if(l==nl and r==nr) return now->val;
		int mid=(nl+nr)/2;
		if(r<=mid) return ask(now->tol,l,r,nl,mid);
		else if(l>mid) return ask(now->tor,l,r,mid+1,nr);
		return ask(now->tol,l,mid,nl,mid)+ask(now->tor,mid+1,r,mid+1,nr);
	}
};
//struct node{
//	int val;
//	int tol,tor;
//};
//vector<node>t;
//int newnode(){
//	t.push_back({0,0,0});
//	return t.size()-1;
//}
//class Stree{
//	public:
//	int root=0;
//	void change(int &now,int pos,int val,int l,int r){
//		if(!now) now=newnode();
//		if(l==r){
//			t[now].val+=val;
//			return;
//		}
//		int mid=(l+r)/2;
//		if(pos<=mid) change(t[now].tol,pos,val,l,mid);
//		else change(t[now].tor,pos,val,mid+1,r);
//		t[now].val=t[t[now].tol].val+t[t[now].tor].val;
//	}
//	int ask(int now,int l,int r,int nl,int nr){
//		if(!now) return 0;
//		if(l==nl and r==nr) return t[now].val;
//		int mid=(nl+nr)/2;
//		if(r<=mid) return ask(t[now].tol,l,r,nl,mid);
//		else if(l>mid) return ask(t[now].tor,l,r,mid+1,nr);
//		return ask(t[now].tol,l,mid,nl,mid)+ask(t[now].tor,mid+1,r,mid+1,nr);
//	}
//};
class BIT{
	public:
	Stree a[200001];
	inline int lowbit(int x){return x&-x;}
	inline void add(int posx,int posy,int val){
		for(int i=posx;i<=k;i+=lowbit(i)){
			a[i].change(a[i].root,posy,val,1,k);
		}
	}
	inline int ask(int x,int y){
		int ans=0;
		for(int i=x;i;i-=lowbit(i)){
			ans+=a[i].ask(a[i].root,1,y,1,k);
		}
		return ans;
	}
}bit;
struct un{
	int x,y,z;
	bool operator <(const un &A)const{
		if(x!=A.x) return x<A.x;
		if(y!=A.y) return y<A.y;
		return z<A.z;
	}
	bool operator ==(const un &A)const{
		return x==A.x and y==A.y and z==A.z;
	}
}d[200001];
int ans[200001];
int main(){
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;++i){
		scanf("%d %d %d",&d[i].x,&d[i].y,&d[i].z);
	}
	sort(d+1,d+n+1);
	int t=1;
	for(int i=1;i<=n;++i){
		if(d[i+1]==d[i]){
			t++;
			continue;
		}
		bit.add(d[i].y,d[i].z,t);
		int res=bit.ask(d[i].y,d[i].z);
		ans[res]+=t;
		t=1;
	}
	for(int i=1;i<=n;++i){
		printf("%d\n",ans[i]);
	}
}
posted @ 2024-08-09 17:38  HaneDaniko  阅读(16)  评论(0编辑  收藏  举报