顯然這些值會構成一個 heap 。首先觀察到一件重要的事實:如果一個節點 u 的值確定了,但他的子節點個數 > 1,那麼 u 的所有子孫一定都是不確定的。因為對於 u 的任意一個子孫 v,我們一定可以把他和 u 的另一個子樹裡的點的值交換( 滿足交換的兩個節點的 LCA 是 u ),並且用維護 heap 的方法把該往上浮的往上浮,該往下沉的往下沉,就可以讓 v 上的值換成另一個值了。而這個交換的動作不會出問題是因為交換的兩個數都 < u ,所以可以保證交換之後往上浮的數不會把 u 給取代掉。
以下稱原本就已經確定值的點為黑點。再來會發現,我們可以把所有連接兩個黑點的邊全部都拔掉,把一顆樹拔成森林,因為對於拔完之後的每個子樹來說,他原本是落在拔邊之前的樹的哪裡並不重要,因為黑點們已經好好的滿足 heap 的性質了,重要的只剩下這棵子樹的形狀,和根上標的值了( 根是黑點 )。這邊我們會需要保證拔完之後的每個子樹的根都是黑的(並且他的子孫都不是黑的),而不滿足這個條件的情況只有整棵樹都不是黑的,那麼對這個情況特別處理即可。
所以現在我們獲得了好幾個只有根為黑色的子樹,把他們按照根的值從小到大排序,記排完序後的子樹們分別是 T_1 , T_2 , ... , T_m ,並且 T_i 的根的值是 v_i ,還有所有還沒使用過的數的集合為 S ( 就是 1~n 扣掉黑點上已經標好的數們 )。先看 T_1 ,如果在 S 中小於 v_1 的數的個數和 T_1 中未確認的數的個數一樣多的話,那麼就可以確定 T_1 中未確認的數就是所有小於 v_1 的 S 裡的數。而對於每一個數我們可以先預處理出「小於他的在 S 裡的數有幾個」,並且對於每個子樹預處理出他的 size ,這樣「這個子樹中還沒確定的數的個數」就是 size -1 個,所以可以快速的判斷 T_1 是否滿足上述條件。所以當我們知道 T_1 中只有哪些數的時候,如果 T_1 的根有 > 1 個孩子,那麼這整棵樹的值的位置都是不確定的( 原因在一開始就有提到 ),而如果只有 1 個孩子的話,那麼根的孩子這個位置的數就確定了,他一定會是小於 v_1 的最大的還沒被用過的數。所以我們可以一路從 T_1 的根往下走,如果只有一個孩子就繼續走,沿路上遇到的都是值確定的節點,把他們的答案標記好即可。
如果小於 v_1 的 S 裡的數的個數不等於 T_1 中未確認的數的個數,那麼因為題目保證必定存在一種填法使得原本的樹形成一個 heap ,可以知道這時候必定有「小於 v_1 的 S 裡的數的個數」 > 「 T_1 中未確認的數的個數」,否則用那些數字就會沒辦法填完 T_1 底下的空格,產生矛盾。這時候T_1 裡的數一定全部都是位置不確定的,因為這時候必定有一個 < v_1 的數被放到其他子樹去了,那麼對於任意一個目前放在 T_1 裡的數,我們可以把他和放在別的子樹裡的某個 < v_1 的數交換,並浮沉維護好堆性質,並且一樣是因為交換過來的數 < v_1 ,所以可以保證他不會取代掉根。
於是由以上的結論就得到了這樣的算法:如果 k 是最小的數,滿足 T_1 , T_2 , ... , T_k 中,未確認的數的個數總和和小於 v_k 的 S 裡的數的個數相等 ( 注意到這樣的 k 必定存在,因為當 k = m ( 子數總個數 ) 的時候這件事顯然是對的 ),那麼 T_1 ~ T_(k-1) 中的所有數都是不確定的,而從 T_k 的根開始一路往下走,並且保持路上經過的節點都只有一個子節點,那麼路上的所有數都會是確定的。並且從 v_k 開始往下找哪個數還沒用過,依次填入 T_k 中。作完之後就會剩下 T_(k+1) ~ T_m ,而這時的情況和原本有 T_1 ~ T_m 的情況無異,所以可以套用一樣的算法,直到處理完所有的子樹為止。
但這樣做還有一個小問題,就是如果我們在把一個數 x 填入 T_k 時,發現 x < v_( k-1 ) ,那麼 x 就會變成不確定的了,因為這時 x 也可以被丟到 T_(k-1) 去,所以只要在出現這種情況的時候趕快 break 掉即可。
code :
#include<bits/stdc++.h> using namespace std; const int maxn=1000000+10 ; struct P { int id,val ; bool operator < (const P &rhs) const { return val<rhs.val ; } }; int val[maxn],fa[maxn],sz[maxn] ; vector<int> v[maxn] ; void dfs(int x) { sz[x]=1 ; for(int i=0;i<v[x].size();i++) dfs(v[x][i]) , sz[x]+=sz[v[x][i]] ; } P p[maxn] ; int num[maxn] ; main() { int n,root ; scanf("%d",&n) ; for(int i=1;i<=n;i++) num[i]=1 ; bool none=0 ; for(int i=1;i<=n;i++) { scanf("%d%d",&fa[i],&val[i]) ; if(val[i]) num[val[i]]=0 ; if(fa[i]==i && !val[i]) none=1 , root=i ; if(fa[i]!=i && !val[i]) v[fa[i]].push_back(i) ; else fa[i]=i ; } for(int i=1;i<=n;i++) num[i]+=num[i-1] ; if(none) { for(int i=root,j=n;;j--) { val[i]=j ; if(v[i].size()!=1) break ; i=v[i][0] ; } for(int i=1;i<=n;i++) printf("%d\n",val[i]) ; return 0 ; } for(int i=1;i<=n;i++) if(fa[i]==i) dfs(i) ; int cnt=0 ; for(int i=1;i<=n;i++) if(fa[i]==i && !v[i].empty()) p[cnt++]=(P){i,val[i]} ; sort(p,p+cnt) ; for(int i=0,sum=0;i<cnt;i++) { sum+=sz[p[i].id]-1 ; int mi=-1 ; while(sum<num[p[i].val]) mi=p[i].val , i++ , sum+=sz[p[i].id]-1 ; for(int j=p[i].id,now=p[i].val;;now--) { if(v[j].size()!=1) break ; while(num[now]==num[now-1]) now-- ; j=v[j][0] ; if(now<mi) break ; else val[j]=now ; } } for(int i=1;i<=n;i++) printf("%d\n",val[i]) ; }
沒有留言:
張貼留言