題意:給你一棵樹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))