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))