DS x
數據結構課後題整理....只會一個是什麽鬼....
染色問題:
線段樹? 功能太強大了! 我們並不需要那麽多的功能
運用並查集!!!
將相同的並為一段
BZOJ 2375(講真我沒找到這個題在哪裏...)
(其實是因為我沒有權限號啦~)
題目大概是這樣的:
小t非常喜愛畫畫,但是他還是一個初學者.他最近費盡千辛萬苦才拜到已仙逝的達.芬奇為師(神仙?妖怪?謝謝).
達.芬奇果然是畫雞蛋長大的,讓小t一入門就拿著一張白紙條瘋狂地塗色.
假設紙條被劃分成了n個區域,用1~n的整數從左到右順序編號,達芬奇總共下達了m條指令.
第I條指令是讓小t把編號為(I*p+q)mod n+1與(I*q+p)mod n+1(p,q為常整數)之間的區域(連續的一段區域)塗成第I種顏色.
你可以假設達芬奇家中顏料的顏色數足夠多(達芬奇是畫雞蛋長大的).
現在由於達芬奇下達的指令過多,小t一時應付不過來.達芬奇只讓他回答每一個區域最後的顏色. 趁達芬奇還在“五谷輪回之所”忙碌時,小t偷偷的請讓你這個計算機高手幫他算出最後的顏色狀態,並告訴他.時間緊迫,要快喲!(達芬奇的指令次數多到惡心)是真的惡心輸入格式
文件僅一行,為四個整數n,m,p,q。
輸出格式
文件共n行,第i行代表最後第i個格子的顏色。白色編號為0
輸入樣例
4 3 2 4(假樣例....)
輸出樣例
2
2
3
0
數據範圍
20%數據滿足:1≤n≤1000,1≤m≤10000
40%數據滿足:1≤n≤10000,1≤m≤100000
100%數據滿足:1≤n≤1000000,1≤m≤10000000;1≤m*p+q,m*q+p≤231-1
/*思路+小技巧*/
因為一開始染好的色,如果後來也將其染色,那麽他以前染過的顏色便會被覆蓋,所以我們大可以不必從前往後進行染色可以考慮從後往前進行,
但是這樣做所掃過的還是每個區間的長度,所以還需要進行優化!
那麽怎麽優化呢?
首先考慮bool數組,記錄下來是否被染過色,如果染過就跳過,但是這樣做還是全部都掃過一遍,在復雜度上是沒有什麽本質上的變化的
所以劃掉
接下來我們需要考慮的是並查集的性質,以及考慮其父親結點,……
正確的思路是這樣的:
一開始先將父結點設置為自己(並查集~)
將被染色的結點的父親結點都手動設置為該染色區間的最後一個的父結點,然後將接下來再次染色的循環從要被染色的結點的父親結點開始。
因為沒有染過色的結點的父親結點一定是自己,但是染過色的的父親結點卻不是自己,是已經染色的區間的最後,會直接蹦到沒有被染色的地方!
所以可以將for循環染色的開始設置為這個結點的父親結點,限制條件是當前長度小於被染色區間的最右邊(及當前被染色區間最後一個結點),下一個變成的是當前的結點的父親的下一個結點的父結點,以此循環...
所以遇到這種題我們需要考慮的就是:盡量不重復刷,(因為只需要知道最後顏色狀態).
代碼如下(其他的就是並查集的操作):
for (int a=m;a>=1;a--) //逆序 { for (int b=getf(l[a]);b<=r[a];b=getf(b+1)) //如上面的解釋 { if (!color[b]) //如果沒有被染色 { color[b]=1;//標記 ans[b]=a;//記錄答案 f[getf(b)]=getf(r[a]);//進行合並 } } }
完整代碼:(已經經過檢驗^_^請放心食用)
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <string> 6 7 using namespace std; 8 9 const int M = 1000002; 10 int n,m,p,q; 11 int f[M]; 12 bool color[M]; 13 int ans[M]; 14 15 inline int read() 16 { 17 int x=0,f=1; 18 char ch=getchar(); 19 20 while(ch<‘0‘||ch>‘9‘) 21 { 22 if(ch==‘-‘)f=-1; 23 ch=getchar(); 24 } 25 26 while(ch>=‘0‘&&ch<=‘9‘) 27 { 28 x=10*x+ch-‘0‘; 29 ch=getchar(); 30 } 31 32 return x*f; 33 } 34 35 inline int getf(int x) 36 { 37 return f[x] == x ? x : f[x] = getf(f[x]); 38 } 39 40 int main() 41 { 42 n=read(); 43 m=read(); 44 p=read(); 45 q=read(); 46 for(int i=1;i<=n+1;i++) f[i]=i; //因為有可能會蹦到n+1位置 47 for(int i=m;i>=1;i--) 48 { 49 int l=(i*p+q)%n+1,r=(i*q+p)%n+1; //題目的變態要求... 50 if(l>r) swap(l,r); //可能會出現"左端點">"右端點"的情況 51 for(int j=getf(l);j<=r;j=getf(j+1)) 52 { 53 if(!color[j]) //如果沒有被染色 54 { 55 color[j]=1; //進行染色 56 f[getf(j)]=getf(r); //將當前被染色的父結點手動設置到最後一個的父結點 57 // f[getf(j)]=getf(j+1); //在網上是有人這樣寫的,那樣比較快一些!但是我沒有理解... 58 ans[j]=i; //記錄答案 59 } 60 // if(getf(1) == n+1) break; //註意當所有位置全部染色後及時退出 61 } 62 } 63 64 for(int i=1;i<=n;i++) printf("%d\n",ans[i]); 65 66 return 0; 67 }^_^
End.
DS x