CDQ分治學習筆記

2021-08-16 01:12:39 字數 3681 閱讀 6958

今天學了一下cdq分治,感覺這東西真的挺好用的,趕緊寫點東西怕以後再忘咯

其實類似於cdq分治的東西在oi早期學排序的時候就應該學過,那就是歸併排序;

歸併排序的原理和cdq分治大體一樣,先劃分成兩個區間,遞迴解決兩邊,再合併起來;

並且用歸併排序求逆序對的時候本質上就是在解決乙個二維偏序的問題;

首先回憶一下歸併排序是怎麼求逆序對的;

(我太弱了不大好描述。。。歸併排序相關網上找找就好了);

這其中就利用了已經排好序的一半區間來更新另一半區間的答案,這就是cdq分治的主要思想;

當要解決的問題有時間先後時(例如 dp,n維偏序),並且後邊的答案不會影響前邊的答案,就可以採用cdq分治;

有一類很經典的三維偏序問題,首先可以考慮樹套樹解決,但那樣太難寫;

在求逆序對的時候有一種樹狀陣列做法,同樣有很優秀的時空複雜度,所以考慮將cdq分治和樹狀陣列結合起來;

cdq分治中數列的順序是第一維,每個位置上的值是第二維,再將每個位置上加乙個數,其在權值樹狀陣列中的先後順序是第三維;

當前兩維都滿足條件時,就呼叫樹狀陣列看第三維是否滿足;

然後歸併的時候將第二維上的值按順序排好,保證更大的區間合併時兩邊的小區間的前兩維已經有序,這樣在cdq分治nlog的基礎上在加乙個log就可以了;

還有一種cdq分治的應用是動態維護凸包;

一般能用單調佇列的斜率優化dp題,必須要都滿足每個點的橫座標單調並且查詢的斜率單調,當其中乙個條件不滿足時就能用cdq分治解決,而兩個條件都不滿足時要多個二分,時間上多個log;

當乙個條件不滿足時,先遞迴處理其中一半區間,算出那一半區間的值後再歸併排序將不滿足的條件排好序使其滿足單調;

例如sdoi2012任務安排;

此題每個點橫座標因為出題人鬼畜而強行不單調;

因為dp是無後效性的,所以處理完dp值後隨便你怎麼排序都無所謂;

所以處理完一半的值後,順帶排好序,由於另一半還沒有動,斜率單調,可以線性求出另一半如果從前一半轉移值是多少,再遞迴處理另一半(本來想用左右區間描述的。。。但這題很鬼畜從後向前轉移)

yy 了一下,如果只是斜率不單調,大概可以先遞迴排序好一段區間的斜率再從前邊斜率是亂的但橫座標是有序的能夠線性求凸包那裡進行轉移;

把今天做的幾道題的**貼一下吧orz

bzoj 2726 sdoi2012任務安排 cdq維護凸包

#include

#include

#include

#include

#include

#define ll long long

#define random(a,b) (a+rand()%((b)-(a)+1))

const

int maxn=300005;

int n;

ll dp[maxn],f[maxn],t[maxn],s;

intid[maxn];

int tp[maxn];

int que[maxn];

double x(int x)

double y(int x)

bool check(int x,int y,int z)

double getval(int i,int j)

bool vs(int x,int y,int i)

void cdq(int l,int r)

for(int k=mid;k>=l;k--)

cdq(l,mid);

int p1=l,p2=mid+1;

for(int i=l;i<=r;i++)

if(p1>mid)tp[i]=id[p2++];

else

if(p2>r)tp[i]=id[p1++];

else

for(int i=l;i<=r;i++)

id[i]=tp[i];

}int main()

bzoj 2683 簡單題 三維偏序

#include

#include

#include

#include

#include

#define ll long long

#define random(a,b) (a+rand()%((b)-(a)+1))

int n;

const int maxn=200005;

struct asd

}q[maxn*4];

int anstot=0;

int xa,ya,xb,yb;

int ans[maxn];

asd tp[maxn*4];

int a[maxn*3];

int lowbit(int

x)void add(int

pos,int v)

int query(int

pos)

void cdq(int l,int r)

else

tp[++num]=q[p2++];}}

while(p1<=mid)

while(p2<=r)

tp[++num]=q[p2++];

}for(int i=l;i<=mid;i++)

int tpn=0;

for(int i=l;i<=r;i++)

} int main()

else

if (q[tot].check==2)

else

break;

}tot--;

cdq(1,tot);

for(int i=1;i<=anstot;i++)printf("%d\n",ans[i]);

return

0;}

bzoj 3262 陌上花開 三維偏序

#include

#include

#include

#include

#include

#define ll long long

#define random(a,b) (a+rand()%((b)-(a)+1))

const int maxn=100005;

struct asd

bool operator ==(const asd &p)const

}q[maxn],tp[maxn];

int n,k,rans[maxn];

int a[maxn*2];

int lowbit(intx)

void add(int

pos,int v)

int query(int

pos)

void cdq(int l,int r)

else

}while(p1<=mid)

while(p2<=r)

int tot=0;

for(int i=l;i<=mid;i++)add(q[i].mi,-1);

for(int i=l;i<=r;i++)

q[i]=tp[++tot];

}bool cmp(asd x,asd y)

int main()

}for(int i=0;iprintf("%d\n",rans[i]);

return

0;}

在bz排名還是比較靠前的嘛。。。所以我**習慣應該還不是太差orz

CDQ分治 學習筆記

對於每個 查詢 操作,其結果ans i 1,i 1 ans i 1,i 1 ans i 1,i 1 中所有修改對其造成影響的疊加 這裡的 疊加 需要能夠比較方便的維護,例如sum min max sum min max sum mi n ma x等 定義s ol ve l r solve l,r s...

cdq分治學習筆記

0xff 學習cdq分治的前置知識 分治 歸併排序 分治 分治,字面上的解釋是 分而治之 就是把乙個複雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題 直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合併。度娘 歸併排序 歸併排序是普通分治的一種基本應用。將排序序列分...

CDQ分治學習筆記

cdq分治小結 cdq分治,同機房的大佬看了好幾天了,窩這種蒟蒻也來湊個熱鬧 qaq 引用大佬的話 二維裡面 最簡單的簡化版就是逆序對問題了,可以用樹狀陣列來維護,說他是簡化版其實是因為有一維 下標已經有序了,那麼就去大力 搞 另一維就好了 公升級版 一般的二維偏序問題 思路是一樣的,要通過排序使一...