给定一棵n个点的树,问其中有多少条长度为偶数的路径。路径的长度为经过的边的条数。x到y与y到x被视为同一条路径。路径的起点与终点不能相同。
考虑树形dp,dp[i][0/1]分别表示从子树中到i结点的偶数路径和奇数路径有多少,那么有如下转移:
dp[i][0] += dp[u][1]
dp[i][1] += dp[u][0]。
但是发现对于那些跨子树的没考虑好,后来才想明白,其实跨子树的情况我们把他加到根结点当中在计数就可以了.
也就是
ans += dp[i][0] * dp[u][1]
ans += dp[i][1]*dp[u][0]。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
typedef long long ll;
ll dp[maxn][3],ans;
int n;
vector<int>vt[maxn];
void dfs(int x,int f)
{
dp[x][0] = 1,dp[x][1] = 0;
for(int i = 0;i < vt[x].size();++i)
{
int v = vt[x][i];
if(v == f) continue;
dfs(v,x);
ans += dp[x][1] * dp[v][0];
ans += dp[x][0] * dp[v][1];
dp[x][0] += dp[v][1];
dp[x][1] += dp[v][0];
}
return ;
}
int main()
{
while(scanf("%d",&n) != EOF)
{
for(int i = 0;i <= n;++i) vt[i].clear();
for(int i = 1;i < n;++i)
{
int x,y;
scanf("%d %d",&x,&y);
vt[x].push_back(y);
vt[y].push_back(x);
}
ans = 0;
dfs(1,-1);
printf("%lld\n",ans);
}
return 0;
}
/*
7
1 2
1 3
2 4
2 5
3 6
3 7
4
1 2
1 3
1 4
5
1 2
1 3
1 4
2 5
*/
另一种比较好的方法就是,考虑随便找一个根节点建树,记录深度,将所有偶数深度的结点拿出来放在一起,个数为a,奇数同理,个数为b,这样就保证了两个图当中任意两个结点之间的路径都为偶数,然后所有路径数为 a∗(a−1)2+b∗(b−1)2
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
typedef long long ll;
vector<int>vt[maxn];
ll a,b;
int n;
void dfs(int x,int f,int dep)
{
if(dep % 2 == 0) a++;
else b++;
for(int i = 0;i < vt[x].size();++i)
{
int v = vt[x][i];
if(v == f) continue;
dfs(v,x,dep+1);
}
return ;
}
int main()
{
while(scanf("%d",&n) != EOF)
{
for(int i = 0;i <= n;++i) vt[i].clear();
for(int i = 1;i < n;++i)
{
int x,y;
scanf("%d %d",&x,&y);
vt[x].push_back(y);
vt[y].push_back(x);
}
dfs(1,-1,0);
//for(int i = 1;i <= n;++i)
// ans += dp[i][0];
ll ans = a*(a-1)/2 + b*(b-1)/2;
printf("%lld\n",ans);
}
return 0;
}