题解 CF1060E 【Sergey and Subway】

eee_hoho

2020-11-19 09:38:40

Solution

说个和别的题解不一样的换根dp方法。 首先分析题目发现可以把长度为$2$的路径走成$1$步,所以我们考虑设$f_{u,0/1}$表示以$u$为根的子树,$u$到所有距离自己为偶数$/$奇数的儿子的答案和,$size_{u,0/1}$表示以$u$为根的子树,距离$u$为偶数$/$奇数的儿子个数,于是有转移方程: $$\begin{cases}f_{u,0}=\sum_{v\in son(u)}f_{v,1}\\f_{u,1}=\sum_{v\in son(u)}f_{v,0}+size_{v,0}\\size_{u,0}=1+\sum_{v\in son(u)}size_{v,1}\\size_{u,1}=\sum_{v\in son(u)}size_{v,0}\end{cases}$$ 具体来说就是从儿子和自己奇偶性相反的转移过来。 如果儿子是偶数,那么从$u$走到儿子需要多走一步;而如果儿子是奇数,那么儿子一定是走了一个长度为$1$的边,所以我们直接走长度为$2$的边,不会产生多的步数。 $size_u,0$里的$+1$是要算上自己。 然后我们再通过一遍换根可以算出来$g_{u,0/1}$表示以$u$为根的树,$u$到所有距离自己为偶数$/$奇数的儿子的答案和。 然后$ans$显然就是$\frac{\sum_{i=1}^ng_{i,1}+g_{i,0}}{2}$。 **Code** ``` cpp #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> const int N = 2e5; using namespace std; int n,edge[N * 2 + 5],nxt[N * 2 + 5],head[N + 5],edge_cnt,size[N + 5][2],dep[N + 5],ss[N + 5][2]; long long f[N + 5][2],ans,g[N + 5][2]; void add_edge(int u,int v) { edge[++edge_cnt] = v; nxt[edge_cnt] = head[u]; head[u] = edge_cnt; } void dfs1(int u,int fa) { size[u][0] = 1; dep[u] = dep[fa] + 1; for (int i = head[u];i;i = nxt[i]) { int v = edge[i]; if (v == fa) continue; dfs1(v,u); f[u][0] += f[v][1]; f[u][1] += f[v][0] + size[v][0]; size[u][0] += size[v][1]; size[u][1] += size[v][0]; } } void dfs2(int u,int fa) { g[u][0] = f[u][0]; g[u][1] = f[u][1]; ss[u][1] = size[u][1]; ss[u][0] = size[u][0]; if (fa) { g[u][0] += g[fa][1] - (f[u][0] + size[u][0]); g[u][1] += g[fa][0] - (f[u][1]) + ss[fa][0] - size[u][1]; ss[u][0] += ss[fa][1] - size[u][0]; ss[u][1] += ss[fa][0] - size[u][1]; } ans += g[u][0] + g[u][1]; for (int i = head[u];i;i = nxt[i]) { int v = edge[i]; if (v == fa) continue; dfs2(v,u); } } int main() { scanf("%d",&n); int u,v; for (int i = 1;i < n;i++) { scanf("%d%d",&u,&v); add_edge(u,v); add_edge(v,u); } dfs1(1,0); dfs2(1,0); cout<<ans / 2<<endl; return 0; } ```