三维偏序——整体二分

整体二分是世界上实用的算法。

不知道整体二分的可以看看这个

使用算法

整体二分、排序、树状数组,没了。

思考

在我们所知道的降维方法里,我们会发现整体二分可以跟着树状数组一起用(其实也可以跟排序一起)做二维偏序,而众所周知树状数组又可以跟排序一起用做二维偏序。

那么我们就想如何将他们两个融为一体。

二维偏序

给定数组 a i a_i ai b i b_i bi,求 a j ≤ a i a_j \le a_i ajai b j ≤ b i b_j \le b_i bjbi 的个数。

整体二分与树状数组

我所定义的整体二分的操作数组的结构体

struct node{
    int x1 , x2 , i , opt ;//x1为a[i] x2为b[i] i是求答案时数组顺序编号 opt为操作:1为原序列 2为查询答案
};
初始化
  1. 将所有的 a i a_i ai b i b_i bi 存入操作数组, o p t opt opt 均为 1。

  2. 将所有的 a i a_i ai b i b_i bi 再次存入操作数组, o p t opt opt 均为 2。

操作
  1. o p t = 1 opt=1 opt=1,如果 m i d ≥ x 2 mid \geq x2 midx2 那么将树状数组数组 t r x 1 tr_{x1} trx1 加 1。

  2. o p t = 2 opt=2 opt=2,就是完全按照树套树用整体二分做的做法求排名(因为有了树状数组提供的没它大的且 b i b_i bi 没他大的 a i a_i ai 个数,问题就被转化成了求 b i b_i bi 的排名)。

代码
void solve(int l,int r,int L,int R){
	if(L>R) return ;
	if(l==r) return ;
	int mid=(l+r)>>1 , tot1=0 , tot2=0 ;
	for(int i=L;i<=R;i++){
		if(a[i].opt==1){
			if(mid>=a[i].x2) change(a[i].x1,a[i].k) , q1[++tot1] = a[i] ;
			else q2[++tot2] = a[i] ;
		}else if(a[i].opt==2){
			if(mid>a[i].x2) q1[++tot1] = a[i] ;
			else{
				ans[a[i].i] += ask(a[i].x1) ;
				q2[++tot2] = a[i] ;
			}
		}
	}
	for(int i=1;i<=tot1;i++){
		a[i+L-1] = q1[i] ;
		if(q1[i].opt==1) change(q1[i].x1,-q1[i].k) ;
	}
	for(int i=1;i<=tot2;i++) a[i+L+tot1-1] = q2[i] ;
	solve(l,mid,L,L+tot1-1) ;
	solve(mid+1,r,L+tot1,R) ;
}

整体二分与排序

其实这玩意有点多此一举

这个就更简单了,按从小到大(从大到小其实也可以但是不好操作)排序之后因为当前 i i i 能与之相匹配的 j j j 一定再他前面所以初始化的使用后是将排序后的数组没添加一个操作 1 就添加一个操作 2。

三维偏序

按照整体二分与排序我们会发现,排序降一维后,初始化会发生改变,但是不影响整体二分与树状数组的操作,所以只需要将排序后的数组,每一次操作 1,就添加一次操作 2。

代码

#include<bits/stdc++.h>
#define int long long
#define N 212345
#define lowbit(x) ((x)&(-x))
#define inf 2147483647
using namespace std ;
int n , K , tr[N] , cnt=0 , ans[N] , real_ans[N] , q , num[N] ;
void change(int x,int k){
	while(x<=K){
		tr[x] += k ;
		x += lowbit(x) ;
	}
}
int ask(int x){
	int ans=0 ;
	while(x){
		ans += tr[x] ;
		x -= lowbit(x) ;
	}
	return ans ;
}
struct user8341006{ //CS2启动
	int a , b , c ;
	bool operator==(const user8341006 &num){
		return this->a==num.a&&this->b==num.b&&this->c==num.c ;
	}
}f[N];
bool cmp(user8341006 a,user8341006 b){return a.a==b.a?(a.b==b.b?a.c<b.c:a.b<b.b):a.a<b.a ;}
struct node{
	int x1 , x2 , opt , i , k ;
}a[N],q1[N],q2[N];
void solve(int l,int r,int L,int R){
	if(L>R) return ;
	if(l==r) return ;
	int mid=(l+r)>>1 , tot1=0 , tot2=0 ;
	for(int i=L;i<=R;i++){
		if(a[i].opt==1){
			if(mid>=a[i].x2) change(a[i].x1,a[i].k) , q1[++tot1] = a[i] ;
			else q2[++tot2] = a[i] ;
		}else if(a[i].opt==2){
			if(mid>a[i].x1) q1[++tot1] = a[i] ;
			else{
				ans[a[i].i] += ask(a[i].x2) ;
				q2[++tot2] = a[i] ;
			}
		}
	}
	for(int i=1;i<=tot1;i++){
		a[i+L-1] = q1[i] ;
		if(q1[i].opt==1) change(q1[i].x1,-q1[i].k) ;
	}
	for(int i=1;i<=tot2;i++) a[i+L+tot1-1] = q2[i] ;
	solve(l,mid,L,L+tot1-1) ;
	solve(mid+1,r,L+tot1,R) ;
}
signed main(){
	cin >> n >> K ;
	for(int i=1;i<=n;i++) scanf("%lld%lld%lld",&f[i].a,&f[i].b,&f[i].c) ;
	sort(f+1,f+1+n,cmp) ;
	for(int i=1;i<=n;i++){
		int number=1 ;
		a[++cnt].opt = 2 ;
		a[cnt].x1 = f[i].c , a[cnt].i = ++q , a[cnt].x2 = f[i].b ;
		a[++cnt].opt = 1 ;
		a[cnt].x1 = f[i].b , a[cnt].x2 = f[i].c ;
		while(f[i]==f[i+1]) number++ , i++ ; //没想到吧跟 cdq 差不多也需要去重
		a[cnt].k = number ;
		num[q] = number ;
		ans[a[cnt-1].i] += number-1 ;
	}
	solve(1,inf,1,cnt) ;
	for(int i=1;i<=q;i++) real_ans[ans[i]] += num[i] ;
	for(int i=0;i<n;i++) printf("%lld\n",real_ans[i]) ;
	return 0 ;
}//maybe 我没挖坑

貌似这玩意是全网首发……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值