dsu on tree(Educational Codeforces Round 2: E. Lomsat gelral)
阿新 • • 發佈:2018-11-10
題意:
一棵n個節點的樹,每個節點都有一種顏色,如果顏色c在以u為根的子樹中出現的次數大於等於一半,那麼這個顏色就是u節點的支配色, 因為是大於等於,所以一個節點的支配色可能不止一種,求出每個節點的支配色編號和
思路:
一個無腦的暴力:
- DFS整棵樹,對於當前節點u,再DFS下以它為根的子樹內所有的節點,相當於DFS套DFS
- 複雜度O(n²),當整棵樹為一條鏈時複雜度最高
優化一下上面的暴力:
- 考慮DFS的過程,對於節點u的所有兒子~,一定是將前一個兒子的子樹全部搜完,才開始搜下一個兒子
- 當然,每當計算完以
- 然而在搜完最後一個兒子後,並不需要刪除貢獻!因為緊接著你要計算的是以u為根的子樹的答案,是被包在裡面的
這樣的話,當一條鏈的時候,原先是最壞情況,在優化之後就成了最好情況,O(n)就能解決
不過複雜度好像仍然是O(n²)?
……
其實到這裡dsu on tree就已經講完了
因為dsu on tree就是上述的暴力,唯一的區別是:對於當前節點u,最後一個搜尋的兒子一定是它的重兒子
可以證明這樣暴力的話複雜度是O(nlogn)的,當然這裡就不證了,證明依賴於樹鏈剖分
#include<stdio.h> #include<string.h> #include<algorithm> #include<map> #include<string> #include<math.h> #include<queue> #include<stack> #include<iostream> using namespace std; #define LL long long #define mod 1000000007 vector<int> G[100005]; int col[100005], siz[100005], hson[100005], vis[100005], all[100005], bet; LL ans[100005], sum; void Sech1(int u, int p) { int i, v; siz[u] = 1; for(i=0;i<G[u].size();i++) { v = G[u][i]; if(v==p) continue; Sech1(v, u); siz[u] += siz[v]; if(siz[v]>siz[hson[u]]) hson[u] = v; } } void Gao(int u, int p) { int i, v; all[col[u]] += 1; if(all[col[u]]>bet) bet = all[col[u]], sum = 0; if(all[col[u]]>=bet) sum += col[u]; for(i=0;i<G[u].size();i++) { v = G[u][i]; if(v==p || vis[v]) continue; Gao(v, u); } } void Init(int u, int p) { int i, v; sum = bet = 0; all[col[u]] = 0; for(i=0;i<G[u].size();i++) { v = G[u][i]; if(v==p) continue; Init(v, u); } } void Sech2(int u, int p) { int i, v; for(i=0;i<G[u].size();i++) { v = G[u][i]; if(v==p || hson[u]==v) continue; Sech2(v, u); Init(v, u); } if(hson[u]) Sech2(hson[u], u), vis[hson[u]] = 1; Gao(u, p), vis[hson[u]] = 0; ans[u] = sum; } int main(void) { int x, y, n, i; scanf("%d", &n); for(i=1;i<=n;i++) scanf("%d", &col[i]); for(i=1;i<=n-1;i++) { scanf("%d%d", &x, &y); G[x].push_back(y); G[y].push_back(x); } Sech1(1, 0); Sech2(1, 0); for(i=1;i<=n;i++) printf("%lld ", ans[i]); puts(""); return 0; }