一道简单的树形dp~
求路径长度为偶数的路径数量,我们可以转化为求路径长度模2等于0的路径数量,这样就好做了~
我们设表示i的子树中,到i的路径长度模2等于0的路径数量
同理,就是模2等于1的路径数量了~
我们想想转移:
我们用的一个儿子来更新,那么,我们可以轻松发现,到的路径长度为奇数的,到就为偶数了(因为多走了一步);到为偶数的,到就变成了奇数。所以,我们有转移方程:
好了,求出这个后,我们就来想想怎么求答案。
我们可以考虑枚举每个路径的lca,这样,我们发现,我们其实需要用的就是dp值了~
我们假设,我们现在枚举到的lca是,我们假设,我们现在添加了一个新的儿子,要算关于之前已添加点的贡献,怎么办?
首先,假设不与其他儿子合并,那么答案就是
如果和其他儿子合并呢?假设之前已经添加好并计算好贡献的儿子分别为
我们发现,答案就是:
我们把答案整理下,就是:
我们发现,我们的只需要知道之前我们计算好贡献的儿子的dp值的和即可直接获得现在这个儿子的贡献了
我们观察后发现,这个和,不就是我们dfs时,算的的值吗?所以,我们可以非常简单的在dfs算,同时计算答案~
代码:
#include<bits/stdc++.h> using namespace std; const int N=1e5+1; #define int long long struct node{ int v,nex; }t[N<<1]; int las[N],len; int dp[N][2]; inline void add(int u,int v){ t[++len]=(node){v,las[u]},las[u]=len; } long long ans; inline void dfs(int now,int fa){ dp[now][0]=1; for(int i=las[now];i;i=t[i].nex){ int v=t[i].v; if(v!=fa){ dfs(v,now); ans+=(dp[v][0]*dp[now][1]);ans+=(dp[v][1]*dp[now][0]); dp[now][0]+=dp[v][1];dp[now][1]+=dp[v][0]; } } } signed main(){ int n; scanf("%lld",&n); for(int i=1;i<n;++i){ int u,v; scanf("%lld%lld",&u,&v); add(u,v),add(v,u); } dfs(1,1); printf("%lld",ans); return 0; }