這種演算法能夠解決關於詢問一棵樹的子樹的相關資訊的問題。
演算法的流程大概是這樣:
1、dfs將一棵樹建好,將節點的size、dfs序、重兒子、該dfs序對應的節點這些資訊處理好(其他的資訊具體問題具體分析)。
2、進入solve函式,先去解決非重兒子,然後將這些非重兒子的資訊暴力清空。
3、接下來解決重兒子,這次不清空。
4、然後再將非重兒子的資訊再暴力新增。
5、將該節點的資訊新增進容器。
6、回答關於這個節點的問題。
7、返回。
當然對於存資訊的sum不一定是普通的陣列,可能是map,可能是樹狀陣列這類既支援插入又支援刪除的容器,有適合的資料結構,而不是用資料結構去套題。
複雜度:
對於某個葉子節點來說,我們最終是要把它對答案的貢獻加到根節點中,從而回答根節點的詢問。可以知道,所有的節點其實都在重鏈上,
葉子節點到根節點的最多經過logn條輕邊,我在將這個節點合併到根的時候,也順便將別的節點合併到根上了,每合併一次,複雜度最多o(n)。
總的複雜度應該是o(nlogn)。
例題:codeforces 1009f
#includeusingnamespace
std;
const
int maxn = 1e6 + 5
;int
deep[maxn],num[maxn],son[maxn],ans[maxn],n,mx;
mapmp;
vector
graph[maxn];
void
init()
void add(int cur,int
fa)}
void dfs(int cur,int
fa)
if(son[cur] != -1
) dfs(son[cur],cur);
for(int i = 0;i < graph[cur].size();++i)
mp[deep[cur]] += 1
;
if(mp[deep[cur]] > mp[mx] || (mp[deep[cur]] == mp[mx] && deep[cur] < mx)) mx =deep[cur];
ans[cur] = mx -deep[cur];
}void dfs1(int cur,int fa,intd)}
intmain()
init();
dfs1(
1,0,1
); dfs(
1,0);
for(int i = 1;i <= n;++i) printf("
%d\n
",ans[i]);
return0;
}
codeforces 600e
#includeusingnamespace
std;
typedef
long
long
ll;const
int maxn = 1e5 + 5
;ll color[maxn],ans[maxn];
mapint>mp;
mapmp2;
vector
graph[maxn];
intdeep[maxn],num[maxn],son[maxn],mx;
//map::iterator iter;
void add(int cur,int
fa)}
void dfs(int cur,int
fa)
if(son[cur] != -1
) dfs(son[cur],cur);
for(int i = 0;i < graph[cur].size();++i)
int keep = ++mp[color[cur]];
if(keep > mx) mx =keep;
mp2[keep] +=color[cur];
ans[cur] =mp2[mx];
}void dfs1(int cur,int fa,intd)}
intmain()
dfs1(
1,0,1
); dfs(
1,0);
for(int i = 1;i <= n;++i)
printf(
"%lld%c
",ans[i],i == n ? '
\n' : '');
return0;
}
codeforces 570d
#includeusingnamespace
std;
const
int maxn = 5e5 + 5
;struct
query
};int
deep[maxn],son[maxn],num[maxn],color[maxn],n,m;
char
str[maxn];
bool
ans[maxn];
mapmp;
vector
graph[maxn];
vector
ask[maxn];
inline
int cnt1(int
x)
return
ret;
}void add(int cur,int
fa)}
void dfs(int cur,int
fa)
if(son[cur] != -1
) dfs(son[cur],cur);
for(int i = 0;i < graph[cur].size();++i)
mp[deep[cur]] ^= (1
}void dfs1(int cur,int fa,intd)}
intmain()
scanf("%s
",str + 1
);
for(int i = 1;i <= n;++i) color[i] = str[i] - 'a'
;
for(int i = 0;i < m;++i)
memset(son,-1,sizeof
(son));
dfs1(
1,0,1
); dfs(
1,0);
for(int i = 0;i < m;++i)
}
樹上啟發式合併總結
某一天發現一道樹上啟發式合併裸題,但我不會寫 學習並刷了兩天的題,是時候來寫個總結了 樹上啟發式合併 dsu on tree 是乙個在o n logn o nlogn o nlog n 時間內解決許多樹上問題的有力演算法。但它的中心其實是 暴力!沒錯,它正是由暴力優化而來。我們先看一道例題 cf60...
樹上啟發式合併
解決樹上統計問題,o n log n o n log n o n lo g n 可以結合線段樹等資料結構維護深度上的資訊 部落格 入門題 const int maxn 1e5 7 const int mod 1e9 7 ll n,m,u,v,mx,sum vector int mp maxn int...
樹上啟發式合併
樹上啟發式合併,一種美妙的黑科技,可以用普通的優化讓你 n 2 變成嚴格 n log 解決一些類似 樹上數顏色,樹上查眾數 這樣的問題 首先你要知道暴力為什麼是 n 2 的 以這個圖為例 每次你從乙個節點開始向下搜,你從1節點搜到3,搜完這個子樹然後你需要把3存的col等資訊刪去再遍歷另乙個子樹才是...