整体二分是世界上实用的算法。
不知道整体二分的可以看看这个
使用算法
整体二分、排序、树状数组,没了。
思考
在我们所知道的降维方法里,我们会发现整体二分可以跟着树状数组一起用(其实也可以跟排序一起)做二维偏序,而众所周知树状数组又可以跟排序一起用做二维偏序。
那么我们就想如何将他们两个融为一体。
二维偏序
给定数组 a i a_i ai、 b i b_i bi,求 a j ≤ a i a_j \le a_i aj≤ai 且 b j ≤ b i b_j \le b_i bj≤bi 的个数。
整体二分与树状数组
我所定义的整体二分的操作数组的结构体
struct node{
int x1 , x2 , i , opt ;//x1为a[i] x2为b[i] i是求答案时数组顺序编号 opt为操作:1为原序列 2为查询答案
};
初始化
-
将所有的 a i a_i ai、 b i b_i bi 存入操作数组, o p t opt opt 均为 1。
-
将所有的 a i a_i ai、 b i b_i bi 再次存入操作数组, o p t opt opt 均为 2。
操作
-
当 o p t = 1 opt=1 opt=1,如果 m i d ≥ x 2 mid \geq x2 mid≥x2 那么将树状数组数组 t r x 1 tr_{x1} trx1 加 1。
-
当 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 我没挖坑
貌似这玩意是全网首发……