572 字
3 分钟
算法笔记:dsu on tree(树上启发式合并)
之前写过但是vp一把遇到后发现还是没理解清楚,狠补一下这个。
- 模板题:https://www.luogu.com.cn/problem/U41492
- 2022CCPC女生赛L题:https://codeforces.com/gym/104081/problem/L
- cf rate 1800:https://codeforces.com/contest/161/problem/D
- cf rate 2300:https://codeforces.com/contest/600/problem/E
- cf rate 2200:https://codeforces.com/contest/570/problem/D
- cf rate 2300:https://codeforces.com/contest/1009/problem/F
- cf rate 2100:https://codeforces.com/contest/208/problem/E
参考:https://zhuanlan.zhihu.com/p/658598885
模板题:树上数颜色
给定一棵根节点为 的树,每个节点有不同的颜色。
多次询问,每次询问查询以 节点为根节点的子树中的不同颜色树。
#include <bits/stdc++.h>using namespace std;
//#undef LOCAL_DEBUG#ifdef LOCAL_DEBUG#include "debug.h"#else#define debug(...) (void)0#define debug_array(arr, len) (void)0#define debug_container(container) (void)0#endif
typedef unsigned long long ull;typedef long long ll;typedef pair<int,int> P;const int maxn=1e5;
int n;vector<int> G[maxn+5];int color[maxn+5];
int big[maxn+5];int size[maxn+5];void rebuild(int u,int fa){ size[u]=1; for (int v: G[u]) if (v!=fa){ rebuild(v,u); size[u]+=size[v]; if (size[big[u]]<size[v]) big[u]=v; }}
int cnt[maxn+5];int color_size;void add(int u,int x){ cnt[color[u]]+=x; if (cnt[color[u]]==0) color_size--; else if (x==1&&cnt[color[u]]==1) color_size++;}void update(int u,int fa,int x){ add(u,x); for (int v: G[u]) if (v!=fa) update(v,u,x);}
int ans[maxn+5];void dsu_on_tree(int u,int fa,bool keep){ for (int v: G[u]) if (v!=fa&&v!=big[u]) dsu_on_tree(v,u,false); if (big[u]) dsu_on_tree(big[u],u,true); add(u,1); for (int v: G[u]) if (v!=fa&&v!=big[u]) update(v,u,1); ans[u]=color_size; if (keep==false){ add(u,-1); for (int v: G[u]) if (v!=fa) update(v,u,-1); }}
void solve(void){ cin>>n; for (int i=1,u,v;i<n;i++){ cin>>u>>v; G[u].push_back(v); G[v].push_back(u); } for (int i=1;i<=n;i++) cin>>color[i]; rebuild(1,0); dsu_on_tree(1,0,true); int q,u; cin>>q; while (q--){ cin>>u; cout<<ans[u]<<endl; }}
int main(){ std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); int t=1;// cin>>t; while (t--) solve();// cout<<(solve()?"YES":"NO")<<endl; return 0;}L. 彩色的树
也是统计子树颜色,但是这里的答案,要求是每个子树中深度不超过 的。
可以把每个节点与它深度恰好为 的重子树节点都连起来,然后统计前动态删掉。
#include <bits/stdc++.h>using namespace std;
#undef LOCAL_DEBUG#ifdef LOCAL_DEBUG#include "debug.h"#else#define debug(...) (void)0#define debug_array(arr, len) (void)0#define debug_container(container) (void)0#endif
typedef unsigned long long ull;typedef long long ll;typedef pair<int,int> P;const int maxn=1e5;const int inf=0x3fffffff;
int n,k;int color[maxn+5];
vector<int> G[maxn+5],T[maxn+5];
int sz[maxn+5];int son[maxn+5];void rebuild(int u,int fa){ sz[u]=1; for (int v: G[u]) if (v!=fa){ rebuild(v,u); sz[u]+=sz[v]; if (sz[son[u]]<sz[v]) son[u]=v; }}
int line[maxn+5],len;void connect(int u,int fa){ line[++len]=u; debug(u,len); if (len-k-1>0&&son[line[len-k-1]]==line[len-k]){ debug(line[len-k-1],u); T[line[len-k-1]].push_back(u); } for (int v: G[u]) if (v!=fa) connect(v,u); len--;}
int cnt[maxn+5];int color_sz;
void revise(int u,int x){ int c=color[u]; cnt[c]+=x; if (cnt[c]==0) color_sz--; else if (x==1&&cnt[c]==1) color_sz++;}void update(int u,int fa,int x,int depth,int lim=k){ if (depth>lim) return ; revise(u,x); for (int v: G[u]) if (v!=fa) update(v,u,x,depth+1,lim);}
int ans[maxn+5];void dsu_on_tree(int u,int fa,bool keep){ for (int v: G[u]) if (v!=fa&&v!=son[u]) dsu_on_tree(v,u,false); if (son[u]) dsu_on_tree(son[u],u,true); revise(u,1); for (int v: G[u]) if (v!=fa&&v!=son[u]) update(v,u,1,1); for (int v: T[u]){ debug(u,v); revise(v,-1); } ans[u]=color_sz; if (keep==false){ revise(u,-1); update(son[u],u,-1,1); for (int v: G[u]) if (v!=fa&&v!=son[u]) update(v,u,-1,1); }}
void solve(void){ cin>>n>>k; for (int i=1;i<=n;i++) cin>>color[i]; for (int i=1,u,v;i<n;i++){ cin>>u>>v; G[u].push_back(v); G[v].push_back(u); } rebuild(1,0); connect(1,0); dsu_on_tree(1,0,true); int q,u; cin>>q; while (q--){ cin>>u; cout<<ans[u]<<endl; }}
int main(){ std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); int t=1;// cin>>t; while (t--) solve();// cout<<(solve()?"YES":"NO")<<endl; return 0;} 算法笔记:dsu on tree(树上启发式合并)
https://fuwari.vercel.app/posts/acm/cp-note-dsu-on-tree/