萬字長文·三維偏序/cdq分治
阿新 • • 發佈:2021-06-16
萬字長文·三維偏序/cdq分治
cdq分治
靜態區間問題
cdq分治是一種解決區間統計問題的常用方法
點分治相當於吧cdq分治挪到樹上
cdq分治即把一個序列分為兩部分 區間統計的量分為跨中點的 左邊的 右邊的
(一下所說統計均指在區間中點)
先遞迴處理左邊的和右邊的
然後再尋找某種\(O(n)\)或較優方式統計跨中點的
熟知歸併排序求逆序對
這就是使用cdq分治解決二維偏序的例子
離線的帶修資料結構問題
cdq 分治被稱為 動態問題轉化為靜態問題的工具
發現離線的資料結構離線下來的操作是一個序列
每個修改操作會對它後面的詢問操作產生影響
使用cdq分治來完成這個問題
先遞迴處理[l,mid],[mid+1,r]
然後加入[l,mid]中操作對[mid+1,r]中詢問的影響 並得出答案
正確性:顯然所有修改按時間順序排序
一個例子
矩形加矩形求和¶
這裡的矩形加矩形求和就是字面意思上的矩形加矩形求和,
讓你維護一個二維平面,然後支援在一個矩形區域內加一個數字,
每次詢問一個矩形區域的和
這個問題的不帶修就是經典的掃描線問題
加入修改後只需按照cdq套路完成即可qwq
三維偏序
顯然三維偏序問題是二維偏序的擴充套件
二維偏序有兩種寫法:歸併排序(cdq分治)和樹狀陣列
考慮結合二者
先對第一維排序
然後顯然[l,mid]和[mid+1,r]只會在兩者之間產生偏序
我們可以使用雙指標掃左區間和右區間 同二維偏序一樣
將左區間第二維小於右區間的第三維插入樹狀陣列 將對應於左區間的右區間首個b<左區間點的點的c值在樹狀陣列中查詢字首和
正確性與歸併排序同理
細節:對於相同點 顯然二者可以互相貢獻答案 而該演算法顯然只能計算一次 所以要去重再把重值算回
程式碼
#include<bits/stdc++.h> using namespace std; #define N 200005 struct node { long long a,b,c,cnt,ans=0; }q1[N],q2[N]; long long n,kk,asn[N]; bool cmp(node x,node y) { if(x.a==y.a) { if(x.b==y.b) return x.c<y.c; else return x.b<y.b; } else return x.a<y.a; } bool cmp2(node x,node y) { if(x.b==y.b) return x.c<y.c; else return x.b<y.b; } struct tyz { long long tr[N]; void change(long long x,long long k) { for(;x<=kk;x+=(x&(-x))) tr[x]+=k; } long long ask(long long x) { long long ret=0; for(;x>0;x-=(x&(-x))) ret+=tr[x]; return ret; } void clear() { for(long long i=1;i<=kk;i++) tr[i]=0; } }qt; void cdq(long long l,long long r) { if(l==r) return; long long mid=(l+r)>>1; cdq(l,mid),cdq(mid+1,r); long long ls=l,rs=mid+1;long long i=l; for(;i<=r&&ls<=mid&&rs<=r;) { if(q2[ls].b<=q2[rs].b) qt.change(q2[ls].c,q2[ls].cnt), q1[i].a=q2[ls].a, q1[i].b=q2[ls].b, q1[i].c=q2[ls].c, q1[i].ans=q2[ls].ans, q1[i].cnt=q2[ls].cnt, i++, ls++; else q2[rs].ans+=qt.ask(q2[rs].c), q1[i].a=q2[rs].a, q1[i].b=q2[rs].b, q1[i].c=q2[rs].c, q1[i].ans=q2[rs].ans, q1[i].cnt=q2[rs].cnt, i++, rs++; } while(rs<=r) { q2[i].ans+=qt.ask(q2[rs].c), q1[i].a=q2[rs].a, q1[i].b=q2[rs].b, q1[i].c=q2[rs].c, q1[i].ans=q2[rs].ans, q1[i].cnt=q2[rs].cnt, i++; rs++; } for(int i=l;i<ls;i++) qt.change(q2[i].c,-q2[i].cnt); while(ls<=mid) { q1[i].a=q2[ls].a, q1[i].b=q2[ls].b, q1[i].c=q2[ls].c, q1[i].ans=q2[ls].ans, q1[i].cnt=q2[ls].cnt, i++; ls++; } for(long long kkk=l;kkk<=r;kkk++) { q2[kkk].a=q1[kkk].a, q2[kkk].b=q1[kkk].b, q2[kkk].c=q1[kkk].c, q2[kkk].ans=q1[kkk].ans, q2[kkk].cnt=q1[kkk].cnt; } } int main() { scanf("%d%d",&n,&kk); for(long long i=1;i<=n;i++) scanf("%d%d%d",&q1[i].a,&q1[i].b,&q1[i].c); sort(q1+1,q1+1+n,cmp); long long m=0,top=0; for(long long i=1;i<=n;i++) { top++; if(q1[i].a!=q1[i+1].a||q1[i].b!=q1[i+1].b||q1[i].c!=q1[i+1].c) { m++; q2[m].a=q1[i].a; q2[m].b=q1[i].b; q2[m].c=q1[i].c; q2[m].cnt=top; top=0; } } cdq(1,m); for(long long i=1;i<=m;i++) asn[q2[i].ans+q2[i].cnt-1]+=q2[i].cnt; for(long long i=0;i<n;i++) cout<<asn[i]<<endl; return 0; }