CDQ分治入门 + 例题 Arnooks's Defensive Line [Uva live 5871]
CDQ分治入門
簡介
CDQ分治是一種特別的分治方法,它由CDQ(陳丹琦)神犇于09國家集訓隊作業中首次提出,因此得名。CDQ分治屬于分治的一種。它一般只能處理非強制在線的問題,除此之外這個算法作為某些復雜算法的替代品幾乎是沒有缺點的。
深入
?????? 對于一個數據結構題而言(或者需要運用數據結構的地方),我們無非就是做兩件操作,一是修改,二是查詢。
?????? 對于修改而言,有插入,刪除,變更(其實等價于刪除再插入)這幾種方式。
?????? 那么查詢的本質是什么呢?我們思考所遇到過的數據結構題,可以發現查詢實際上就在做一件事情:把符合本次查詢的限制的修改對答案產生的效果合并起來滿足。這種限制通常表現為一種序的要求,并且這種序是廣義的,符合限制的操作往往是按某種序(或多種序)排序后的操作的前綴。??????? 通常來說,查詢一定有時間上的限制,也就是要求考慮發生在某個時刻之前的所有查詢,對于一個問題而言,假如所有查詢要求的發生時刻相同,那這就是一個靜態查詢問題,如果要求發生的時刻隨著查詢而變,那這就是一個動態修改問題,動態修改問題較靜態查詢而言復雜很多,往往需要高級數據結構,可持久化等手段,而靜態查詢簡單很多,例如時間倒流,twopointers之類的方法都是很好的選擇。
動態修改->靜態查詢!
?????? CDQ分治算法的核心就在于:去掉時間的限制,將所有查詢要求發生的時刻同化,化動態修改為靜態查詢(其實對于有些問題來說可以把某一維的限制通過排序看作時間限制然后運用CDQ分治)。???????
我們記過程Solve(l,r)表示處理完[l,r]內的修改對查詢的影響。此時我們引入分治思想,將操作序列劃分為[l,mid],[mid+1,r]兩個區間,這兩個/區間內部的修改對區間內部的查詢的影響是完全相同的/的子問題,我們遞歸處理,處理完之后剩下來只要考慮[l,mid]中的修改對[mid+1,r]中的查詢的影響。這時我們發現這其實已經變成了一個靜態查詢問題,因為所有的查詢都發生在修改之后,我們只需要考慮靜態查詢的問題如何處理即可。
具體做法
?????? 考慮這樣一個問題,有若干詢問和若干修改,要求在O(nlogn)時間復雜度內回答所有的詢問。(下把詢問與修改統稱為操作)。
我們把[l,r]代表當前處理的操作的區間,即處理第l個到第r個操作,先找到區間的中間m=(l+r)/2。
(1)然后對于前一半[l,m]我們先遞歸解決。
(2)對于所有在[l,m]內的修改操作,枚舉處理它對于[m,r]內的所有操作影響。
(3)之后遞歸處理[m,r]這一區間。????????
復雜度分析:分治共有log(n)層,那么要求每一層都在線性的時間復雜度內完成,才能保證總時間復雜度是O(nlogn)。???????
對于不同的題目,修改和詢問是不一樣的。在某些修改之間互相獨立的題目下,還可以調換(2)(3)的順序。
當然cdq分治也可以像其他的分治方法一樣,巧妙利用歸并排序,來實現每層O(logn)
?
接下來我們看道題目幫助理解
Arnooks's Defensive Line [Uva live 5871]
Describe (不想看英語的請往下跳到一句話題意)
Based on the latest intelligence reports, Chief Arnook of the northern tribe has become suspicious of the warrior nations that dwell south of the border. The chief has ordered his warriors to protect the southern border which runs parallel to the 54 o latitude line and stretches between the longitudes of 1 o to 1000, 000, 000 o , inclusive.
Each warrior is assigned the task of protecting a segment of the border defined to lie between longitudes a and b, inclusive. No two warriors are assigned to protect the exact same segment. Bound by loyalty to his chief, a warrior will inform the chief upon his arrival at his appointed post and will never leave once he arrives.
Your task is to write a program that performs the following two operations + a b a warrior assumes his position and protects the segment between longitudes a and b, inclusive. ? c d computes the number of warriors who completely protect the segment between longitudes c and d, inclusive. The segment between the longitudes c and d, inclusive, is said to be completely protected by a warrior X if and only if warrior X protects a segment between a and b, inclusive, and a ≤ c < d ≤ b. to help Chief Arnook track the status of his border protection.
Input
The input starts with an integer N (1 ≤ N ≤ 500000), on a line by itself, that indicates the number of operations. Each of the following N lines contains one operation. The description of an operation consists of a character ‘+’ or ‘?’ followed by two integers on a line by itself. The entries on a line are separated by single blank spaces.
Output
There is one output line for each input line that starts with the operation ‘?’. The output consists of a single integer that represents the number of warriors who completely protect the corresponding segment at the time.
There is no output for input lines that start with the character ‘+’.
Sample Input
9
+ 5 10
+ 7 20
+ 3 15
? 9 12
+ 10 20
? 8 9
+ 6 30
? 8 9
? 9 12
Sample Output
2
3
4
3
?
一句話題意
有兩種操作(共n個,n<=500000), 一個是插入一個[l,r]的區間,另一個是詢問一個區間[l,r],前面有多少個區間完全包含了這個區間。
?
思路
法一:很容易想到的一個搞法是線段樹套平衡樹,也就是對于插入的區間,把[1,l]都插入一個r,然后對詢問的區間就是看[1,l]里面有多少個數>=r,然而這樣的空間復雜度是nlog2n,n=50w顯然T飛
法二:換種思路,去掉時間限制,把問題看成一個序列,則前面的修改會對后面的詢問造成影響。于是我們便想到了CDQ分治中第二點:“對于所有在[l,m]內的修改操作,枚舉處理它對于[m,r]內的所有操作影響。”所以我們可以對一個操作序列[a,b], 設m=(a+b)/2,先遞歸處理[a,m], [m+1,b],然后考慮[a,m]里面的插入區間的操作對[m+1,b]里面詢問的影響。也就是轉化成靜態的問題。可以先把區間按照右端點從大到小排序*,然后掃一邊,掃到一個插入區間用樹狀數組就在l處+1,詢問就是原有答案加上sum(l)。
上面*號注意,右端點相同時,左端點一定要從小到大排序,舉個例子,如果一個詢問是 i+1->j,一個插入是i->j,插入一定要排在詢問前面才能被統計到
?
代碼
?
1 #include<set> 2 #include<map> 3 #include<stack> 4 #include<queue> 5 #include<cstdio> 6 #include<cstring> 7 #include<iostream> 8 #include<algorithm> 9 #define RG register int 10 #define rep(i,a,b) for(RG i=a;i<=b;i++) 11 #define per(i,a,b) for(RG i=a;i>=b;i--) 12 #define ll long long 13 #define inf (1<<30) 14 #define maxn 500005 15 #define lowbit(x) (x&(-x)) 16 using namespace std; 17 int n,cnt; 18 int hash_table[maxn<<1],t[maxn<<1],ans[maxn]; 19 struct Dat{ 20 char type; 21 int l,r,id; 22 }dat[maxn]; 23 inline int read() 24 { 25 int x=0,f=1;char c=getchar(); 26 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 27 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 28 return x*f; 29 } 30 31 int pre() 32 { 33 char c; 34 n=read(); 35 rep(i,1,n) 36 { 37 scanf("%c",&c);dat[i].l=hash_table[++cnt]=read(),dat[i].r=hash_table[++cnt]=read(),dat[i].id=i; 38 if(c=='+') dat[i].type=1; 39 } 40 sort(hash_table+1,hash_table+1+cnt); 41 cnt=unique(hash_table+1,hash_table+1+cnt)-hash_table-1; 42 rep(i,1,n) 43 { 44 dat[i].l=lower_bound(hash_table+1,hash_table+1+cnt,dat[i].l)-hash_table; 45 dat[i].r=lower_bound(hash_table+1,hash_table+1+cnt,dat[i].r)-hash_table; 46 } 47 } 48 49 inline bool cmp(const Dat &a,const Dat &b){return a.r==b.r?a.l<b.l:a.r>b.r;} 50 51 void add(int x,int val) 52 { 53 while(x<=cnt) 54 t[x]+=val,x+=lowbit(x); 55 } 56 57 int query(int x) 58 { 59 int sum=0; 60 while(x) 61 sum+=t[x],x-=lowbit(x); 62 return sum; 63 } 64 65 void CDQ(int l,int r) 66 { 67 if(l==r) return; 68 int mid=(l+r)>>1; 69 CDQ(l,mid); 70 CDQ(mid+1,r); 71 sort(dat+l,dat+r+1,cmp); 72 for(int i=l;i<=r;i++) 73 { 74 if(dat[i].type&&dat[i].id<=mid) add(dat[i].l,1); 75 if(!dat[i].type&&dat[i].id>mid) ans[dat[i].id]+=query(dat[i].l); 76 } 77 for(int i=l;i<=r;i++) 78 if(dat[i].type&&dat[i].id<=mid) add(dat[i].l,-1); 79 } 80 81 inline bool cnm(const Dat &a,const Dat &b){return a.id<b.id;} 82 83 int main() 84 { 85 pre(); 86 CDQ(1,n); 87 sort(dat+1,dat+1+n,cnm); 88 rep(i,1,n) if(!dat[i].type) printf("%d\n",ans[i]); 89 return 0; 90 } View Code?
轉載于:https://www.cnblogs.com/ibilllee/p/7678685.html
總結
以上是生活随笔為你收集整理的CDQ分治入门 + 例题 Arnooks's Defensive Line [Uva live 5871]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 画Excel折线图的一点记录
- 下一篇: Dubbo SPI机制学习总结(持续更新