dsu on tree
dsu on tree
流程
- 首先将整棵树进行重链剖分。
- 对于结点 ,我们遍历其轻儿子,并计算答案,但是不保留这些结点的影响。
- 遍历其重儿子,保留其影响。
- 再次遍历 的轻儿子,加入这些结点的贡献,就可以得到 的答案。
复杂度证明
对于一棵 个结点的树,根结点到树上任意结点的轻边数不超过 条。证明如下:
设点 到根结点 有 条轻边数,且 的子树大小为 ,显然轻边连接的子结点的子树大小小于等于其父亲的一半,即若 是 的轻儿子,则有
故
显然 ,故有 。
又因为如果一个结点是其父亲的重儿子,则他的子树必定在他的兄弟之中最多,所以任意结点到根的路径上所有重边连接的父节点在计算答案时必定不会遍历到这个结点,所以一个节点的被遍历的次数等于它到根节点路径上的轻边数 (他本身也要被遍历到),所以一个节点的被遍历次数为 ,故总时间复杂度为 。
#include <bits/stdc++.h>
const int N = 1e6 + 10;
const int M = 2e6 + 10;
inline int read()
{
int cnt = 0; char ch = getchar(); bool op = 1;
for (; ! isdigit(ch); ch = getchar())
if (ch == '-') op = 0;
for (; isdigit(ch); ch = getchar())
cnt = cnt * 10 + ch - 48;
return op ? cnt : - cnt;
}
int n, m, col[N], ans[N];
int nxt[M], head[N], to[M], tot;
inline void add(int u, int v)
{
nxt[++ tot] = head[u];
head[u] = tot;
to[tot] = v;
}
int siz[N], son[N];
inline void dfs1(int u, int fa)
{
siz[u] = 1;
for (int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if (v == fa) continue;
dfs1(v, u);
siz[u] += siz[v];
if (siz[son[u]] < siz[v]) son[u] = v;
}
}
struct query
{
int k, id;
};
std::vector < query > a[N];
int sumcol[N], supersum[N];
int vis[N];
inline void addson(int u, int fa, int val)
{
if (val == -1)
supersum[sumcol[col[u]]] --;
sumcol[col[u]] += val;
if (val == 1)
supersum[sumcol[col[u]]] ++;
for (int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if (v == fa || vis[v]) continue;
addson(v, u, val);
}
}
inline void dfs2(int u, int fa, int op)
{
for (int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if (v == fa || v == son[u]) continue;
dfs2(v, u, 1);
}
if (son[u]) dfs2(son[u], u, 0), vis[son[u]] = 1;
addson(u, fa, 1);
for (int i = 0; i < a[u].size(); ++ i)
ans[a[u][i].id] = supersum[a[u][i].k];
if (son[u]) vis[son[u]] = 0;
if (op) addson(u, fa, -1);
}
int main()
{
n = read(), m = read();
for (int i = 1; i <= n; ++ i)
col[i] = read();
for (int i = 1; i < n; ++ i)
{
int u, v;
u = read(), v = read();
add(u, v); add(v, u);
}
for (int i = 1; i <= m; ++ i)
{
int u, k;
u = read(), k = read();
query t; t.id = i, t.k = k;
a[u].push_back(t);
}
dfs1(1, 0); dfs2(1, 0, 0);
for (int i = 1; i <= m; ++ i)
printf("%d\n", ans[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!