CDQ分治
所以,什么是CDQ分治?
【模板】三维偏序 / 陌上花开
三维偏序,即求同时满足 \(a_i<a_j,b_i<b_j,c_i<c_j\) 的 \((i,j)\) 点对数。
二维偏序,类似逆序对,用树状数组或归并排序均可解决。\(O(n\log n)\)
树状数组
用权值树状数组维护当前位置左侧 大于/小于 某数的数量归并排序
在合并过程中统计右侧区间先于左侧区间加入的元素数量-
按照第一维排序。
-
按照第二维进行归并排序。在合并过程中,左侧区间的 \(a\) 值必然小于右侧区间,那么,当且仅当右侧区间的元素 \(j\) 的 \(b\) 值大于左侧区间元素 \(i\) 时,前两维条件满足。
-
由于两区间均已依照 \(b\) 排序,所以可通过建立以 \(c\) 为下标的树状数组,在合并过程中统计符合第三维条件的点对数量。(若左侧指向元素 \(b\) 值小于等于右侧指向元素,则使树状数组中左侧指向元素 \(c\) 值位置 \(+1\) ,若左侧指向元素 \(b\) 值大于右侧指向元素,则查询树状数组中小于右侧指向元素 \(c\) 值的数量,累加进右侧指向元素的答案中)
时间复杂度 \(O(n\log ^2n)\)。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
int read(){
int i;
scanf("%lld",&i);
return i;
}
int n,k;
class CIS{
array<int,200010> tr={};
public:
void add(int p,int v){
while(p<=k){
tr[p]+=v;
p+=(p&(-p));
}
//for(int i=1;i<=k;i++)printf("%lld%c",tr[i]," \n"[i==k]);
}
int ask(int r){
int ans=0;
while(r){
ans+=tr[r];
r-=(r&(-r));
}
return ans;
}
};
struct Fl{
int a,b,c;
bool operator <(const Fl &f)const{
if(a!=f.a)return a<f.a;
else if(b!=f.b)return b<f.b;
else return c<f.c;
}
bool operator ==(const Fl &f)const{
return a==f.a&&b==f.b&&c==f.c;
}
};
array<Fl,100010> arr;
map<Fl,int> ma;
CIS cs;
int ans[100010]={};
queue<int> remerge(int st,int en){
queue<int> un;
if(st==en){
un.push(st);
return un;
}
int mid=(st+en)/2;
queue<int> p1=remerge(st,mid),p2=remerge(mid+1,en);
queue<int> pp;
while((!p1.empty())&&(!p2.empty())){
if(arr[p1.front()].b>arr[p2.front()].b){
un.push(p2.front()),ans[p2.front()]+=cs.ask(arr[p2.front()].c),p2.pop();
}else{
un.push(p1.front()),cs.add(arr[p1.front()].c,ma[arr[p1.front()]]),pp.push(p1.front()),p1.pop();
}
}
while(!p1.empty())un.push(p1.front()),p1.pop();
while(!p2.empty())un.push(p2.front()),ans[p2.front()]+=cs.ask(arr[p2.front()].c),p2.pop();
while(pp.size())cs.add(arr[pp.front()].c,-ma[arr[pp.front()]]),pp.pop();
return un;
}
int init(){
n=read(),k=read();
for(int i=1;i<=n;i++)arr[i].a=read(),arr[i].b=read(),arr[i].c=read(),ma[arr[i]]++;
sort(arr.begin()+1,arr.begin()+n+1);
unique(arr.begin()+1,arr.begin()+n+1);
int li=ma.size();
queue<int> qu=remerge(1,li);
int num[100010]={};
int cnt=1;
for(map<Fl,int>::iterator it=ma.begin();it!=ma.end();it++,cnt++)num[ans[cnt]+it->second-1]+=it->second;
for(int i=0;i<n;i++)printf("%lld\n",num[i]);
return 3;
}
int three_dimensional_partial_order=init();
signed main(){
return 0;
}
浙公网安备 33010602011771号