1. 程式人生 > >Codeforces 375D 資料結構(好題中的好題, 4解)

Codeforces 375D 資料結構(好題中的好題, 4解)

題意:給你一棵樹n個點,m次詢問(n=100000,m=100000),每個節點有一種顏色, 

           每次詢問問你以v節點為根的子樹中  滿足  同一種顏色的個數>=k的  顏色有幾個。

方法1:顯然詢問要離線處理,不妨用思維簡單的分塊演算法處理詢問,

              對於每個詢問,我們用陣列val[i]表示當前情況下  顏色為i的節點個數

              再用val[i]當做下標,用數狀陣列維護個數的字尾和,

              每次修改一個點  樹狀數組裡面更新2次, 總體複雜度O(sqrt(n)*n*log(n));

方法2: 其實也算方法1的優化, 不同之處只是用一個數組維護字尾和,

               我們發現 每次修改無非就是把val[i]加一或者減一,不妨用陣列sum表示要求的答案

 加1:唯一改變的是  val[i]+1的答案,  這個答案要加1,  即sum[++val[i]]++;

               減1:唯一改變的是  val[i]的答案,   這個答案要減1,  即sum[val[i]--]--;

              總體複雜度O(sqrt(n)*n);

方法3:啟發式合併,   詢問按節點v分類,從葉子節點不斷合併,注意這裡一定要把小的堆合併到大的堆裡面,

             每個子樹用一個平衡樹維護(這裡我用了treap),用另外一個平衡樹維護每個節點u為根的子樹的所有顏色個數

             (我用了map)合併過程就是把兩棵平衡樹合併, treap裡面維護的是val[i],要求答案只要算一下字尾和就可以了

             總體複雜度O(n*log^2(n))

方法4:  分治思想,  同樣詢問按節點v分類,  想想暴力的做法處理詢問,用一個全域性的陣列維護答案,發現O(n^2)可做

               但是會超時, 我們可以做個優化,    對於根u 處理  其子樹v1,v2,v3....時,  我們運算元樹答案的時候,

               把v1子樹放到答案數組裡, 算完以後清空v1子樹, 然後在重複v2,v3....., 注意到最後一個子樹沒有必要清空,

               算完最後一個v後dfs會回溯到上一層,去算上一層的答案,而上一層一定包含了v子樹, 清空了反而複雜度會大大提升,

               所以我們當然是把子樹規模最大的放到最後去處理比較優秀,這裡類似熟練剖分的重鏈,

               每次把子樹加進來和刪除另外寫兩個dfs暴力加減,這樣平均下來每個點被新增和刪除的次數就不會超過log(n)

               這樣一來總體複雜度為O(n*log(n))