树上连通块dp速记
不知道为什么突然就想写点什么,先放两个题目
P2664 树上游戏
[ABC163F] path pass i
也是偶然做到这两个题目,发现有一些相同点,于是就总结一下。
首先,这两个题目都和颜色数有关。
于是想到第一个trick,将每种不同的颜色分开考虑。并从反面的角度(容斥)去考虑
有了这第一个trick以后,这两题都转化为一些关于数上连通块问题,我抽象出来一个简单的模型:
给定一颗涂了颜色的树。求去除某种颜色的节点和相连的边后,每个连通块的大小。
对于每一个节点
图中的根节点为当前的
为什么叶子节点的红色节点不算子树?因为要保证 极大,这个节点已经是另一个根的子树了,所以不是极大。
可能有同学会问,这个东西的定义这么复杂,求它有什么用呢?
设
对于去掉
我们不妨先计算不包括一号节点的连通块。我们dfs每一个节点,对于这个节点的所有儿子节点,如果它和父亲节点颜色不同,他们一定被包括在一个连通块内,而且这样计算是不重不漏的。
void dfs(int u, int fa) {
sz[u]=1;
ll tmp=cnt[col[u]], c=col[u];
for(int v:e[u]) {
if(v==fa) continue ;
int t=cnt[c];
dfs(v, u);
int Ad=cnt[c]-t;
sz[u]+=sz[v];
ans[c]-=Sum(sz[v]-Ad);
}
cnt[c]=tmp+sz[u];
}
我们记一个数组
这样做是为什么呢当前
还有一点,如果连通块包括1号节点,那么我们以下的dfs是找不出来的,但是我们可以增加一个0号节点,他的颜色是所有颜色,连向1号节点。这个0号节点不用建出来,只需要特殊处理一下就行了。
for(int i=1; i<=n; i++)
printf("%lld\n", ans[i]-Sum(n-cnt[i]));
其中