1. 程式人生 > >[bzoj] 3263 陌上花開 洛谷 P3810 三維偏序|| CDQ分治 && CDQ分治講解

[bzoj] 3263 陌上花開 洛谷 P3810 三維偏序|| CDQ分治 && CDQ分治講解

ini down 方法 void 前綴和 大於等於 歸並 定義 修改

原題

定義一個點比另一個點大為當且僅當這個點的三個值分別大於等於另一個點的三個值。每比一個點大就為加一等級,求每個等級的點的數量。


顯然的三維偏序問題,CDQ的板子題。

CDQ分治:
CDQ分治是一種特殊的分治方法,在 OI 界初見於陳丹琦 2008 年的集訓隊作業中,因此被稱為 CDQ 分治。
CDQ分治是將操作分治,用於解決“修改獨立,允許離線”的問題。本質為按時間分治。
可以用CDQ的題目必須滿足:
1、修改與詢問互相獨立,且修改之間互不影響
2、允許離線
那麽我們將操作序列分為兩半。顯然,後一半的操作不會對前一半產生影響,後一半的詢問只受前一半操作和後一半在詢問前的操作的影響。這看起來像是可以遞歸,因為後一半操作序列的修改操作完全不會影響前一半操作序列中的詢問結果,因此前一半操作序列的查詢實際是與後一半操作序列完全獨立的,是與原問題完全相同的子問題,可以遞歸處理。

至此,一個動態修改題變為無動態修改操作的問題,設“解決無動態修改操作的原問題”的復雜度為O(f(n)),那麽由主定理,我們知道這樣分治的總時間復雜度將是O(f(n)logn)。

本題題解 && CDQ分治的一般思路:
1、sort處理一個維度
2、CDQ分治過程中歸並一個維度
3、樹狀數組第三個維度計算答案
本題即為如此(樹狀數組維護前綴和,每次query即為答案,因為是按小到大順序加入的)。

#include<cstdio>
#include<algorithm>
#define N 100010
#define M 200010
using namespace std;
struct
hhh { int x,y,z,cnt,sum; inline bool operator == (const hhh &b) const { return x==b.x && y==b.y && z==b.z; } inline bool operator < (const hhh &b) const { if (x!=b.x) return x<b.x; if (y!=b.y) return y<b.y; return
z<b.z; } inline bool operator > (const hhh &b) const { if (y!=b.y) return y<b.y; return z<=b.z; } }t[N],a[N]; int m,head,tail,ans[N],n,s,f[M]; int read() { int ans=0,fu=1; char j=getchar(); for (;j<'0' || j>'9';j=getchar()) if (j=='-') fu=-1; for (;j>='0' && j<='9';j=getchar()) ans*=10,ans+=j-'0'; return ans*fu; } void init(int x) { while (x<=s) { if (f[x]) f[x]=0; else break; x+=x&-x; } } int query(int x) { int ans=0; while (x) { ans+=f[x]; x-=x&-x; } return ans; } void insert(int x,int y) { while (x<=s) { f[x]+=y; x+=x&-x; } } void CDQ(int l,int r) { if (l==r) return ; int mid=(l+r)>>1,idx1=l,idx2=mid+1; CDQ(l,mid); CDQ(mid+1,r); for (int i=l;i<=r;i++) { if (idx2>r || idx1<=mid && a[idx1]>a[idx2]) { t[i]=a[idx1++]; insert(t[i].z,t[i].cnt); } else { t[i]=a[idx2++]; t[i].sum+=query(t[i].z); } } for (int i=l;i<=r;i++) { a[i]=t[i]; init(a[i].z); } } int main() { m=read(); s=read(); for (int i=1;i<=m;i++) { t[i].x=read(); t[i].y=read(); t[i].z=read(); } sort(t+1,t+m+1); head=1; n=0; while (head<=m) { tail=head+1; while (tail<=m && t[tail]==t[head]) ++tail; a[++n]=t[head]; a[n].cnt=tail-head; head=tail; } CDQ(1,n); for (int i=1;i<=n;i++) ans[a[i].sum+a[i].cnt-1]+=a[i].cnt; for (int i=0;i<m;i++) printf("%d\n",ans[i]); return 0; }

[bzoj] 3263 陌上花開 洛谷 P3810 三維偏序|| CDQ分治 && CDQ分治講解