方法一------简单dfs:
dep表示深度,lca表示最近公共祖先
对于两个点u和v,u到v的路径长度是dep[u] + dep[v] - 2 x dep[lca(u,v)]
很显然 2 x dep[lca(u,v)] 一定是个偶数,所以,要想路径也为偶数,那么dep[u]和dep[v]一定是同为奇数或者同为偶数,所以,只要是深度奇偶性相同的两个点,肯定可以组成一条路径。

由于u->v和v->u只算一次,显然,就是在奇数深度节点中任意取两个,在偶数深度节点中任意取两个,那么就是组合数了,我们只需要求出以任意节点为根,计算深度奇数节点个数cnt1和深度偶数节点个数cnt2即可,答案是cnt1 x (cnt1-1) / 2 + cnt2 x (cnt2-1) / 2;

代码十分简单

#include <cctype>
#include <cfloat>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <algorithm>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <istream>
#include <iterator>
#include <list>
#include <map>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include <unordered_map>
#include <unordered_set>
using namespace std;
const int maxn=1e5+10;
int dep[maxn];
int Head[maxn],tot;
long long ans,cnt1,cnt2;
struct edge {
    int to,Next;
}Edge[maxn<<1];
void add(int from, int to) {
    Edge[tot]={to,Head[from]};
    Head[from]=tot++;
    Edge[tot]={from,Head[to]};
    Head[to]=tot++;
}
void dfs(int u, int fa) {
    dep[u]=dep[fa]+1;
    if (dep[u]%2) cnt1++;
    else cnt2++;
    for (int i=Head[u]; ~i; i=Edge[i].Next) {
        int v=Edge[i].to;
        if (v==fa) continue;
        dfs(v,u);
    }
}
int main() {
    memset(Head,-1,sizeof(Head));
    int n;
    scanf("%d",&n);
    for (int i=1; i<n; i++) {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    dfs(1,0);
    ans=cnt1*(cnt1-1)/2+cnt2*(cnt2-1)/2;
    printf("%lld\n",ans);
    return 0;
}

方法二-------dsu on tree
给自己加难度就用这方法就行了(滑稽)

题意转换求每个节点的子树中路径长度为偶数的数量的总和
看到子树问题,而且是树,可以想想dsu on tree,先划分重链,然后暴力emmm....
直接看代码吧,其实没什么细节,对于每个节点的子树,也是奇+奇,偶+偶这种处理

#include <cctype>
#include <cfloat>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <algorithm>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <istream>
#include <iterator>
#include <list>
#include <map>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include <unordered_map>
#include <unordered_set>
using namespace std;
const int maxn=1e5+10;
int sz[maxn],Son[maxn],dep[maxn];
int Head[maxn],cnt[maxn],tot;
long long ans;
struct edge {
    int to,Next;
}Edge[maxn<<1];
void add(int from, int to) {
    Edge[tot]={to,Head[from]};
    Head[from]=tot++;
    Edge[tot]={from,Head[to]};
    Head[to]=tot++;
}
void dfs(int u, int fa) {
    int Mx=0;
    sz[u]=1;
    dep[u]=dep[fa]+1;
    for (int i=Head[u]; ~i; i=Edge[i].Next) {
        int v=Edge[i].to;
        if (v==fa) continue;
        dfs(v,u);
        sz[u]+=sz[v];
        if (sz[v]>Mx) {
            Mx=sz[v];
            Son[u]=v;
        }
    }
}
void Add(int u, int fa, int val) {
    cnt[dep[u]%2]+=val;
    for (int i=Head[u]; ~i; i=Edge[i].Next) {
        int v=Edge[i].to;
        if (v==fa) continue;
        Add(v,u,val);
    }
}
void calc(int u, int fa) {
    ans+=cnt[dep[u]%2];
    for (int i=Head[u]; ~i; i=Edge[i].Next) {
        int v=Edge[i].to;
        if (v==fa) continue;
        calc(v,u);
    }
}
void solve(int u, int fa, int val) {
    for (int i=Head[u]; ~i; i=Edge[i].Next) {
        int v=Edge[i].to;
        if (v==fa||v==Son[u]) continue;
        solve(v,u,0);
    }
    if (Son[u]) solve(Son[u],u,1);
    for (int i=Head[u]; ~i; i=Edge[i].Next) {
        int v=Edge[i].to;
        if (v==fa||v==Son[u]) continue;
        calc(v,u);
        Add(v,u,1);
    }
    ans+=cnt[dep[u]%2];
    cnt[dep[u]%2]++;
    if (!val) {
        Add(u,fa,-1);
    }
}
int main() {
    memset(Head,-1,sizeof(Head));
    int n;
    scanf("%d",&n);
    for (int i=1; i<n; i++) {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    dfs(1,0);
    solve(1,0,0);
    printf("%lld\n",ans);
    return 0;
}