昨晚上学了二次扫描后,才发现省赛的树形dp题竟然可以用这个写。

我们用f[u] 表示:以节点u为根的递增路径条数。并且计算这些路径相互之间的贡献。

void dfs1(int u, int fa){

    for(int i=0; i<v[u].size(); i++){
        int to=v[u][i];
        if(to==fa){
            continue;
        }

        dfs1(to, u);
        if(u>to){
            s[u]+=f[u]*(f[to]+1);
            f[u]+=(f[to]+1);
        }
    }
}

为什么计算的时候是
s[u]+=f[u]*(f[to]+1);
f[u]+=(f[to]+1);
我们证明一下:

同理二次扫描一样。

#include <bits/stdc++.h>
#define LL long long
using namespace std;
vector<int> v[200005];
LL s[200005];
LL f[200005];
void dfs1(int u, int fa){

    for(int i=0; i<v[u].size(); i++){
        int to=v[u][i];
        if(to==fa){
            continue;
        }

        dfs1(to, u);
        if(u>to){
            s[u]+=f[u]*(f[to]+1);
            f[u]+=(f[to]+1);
        }
    }
}

void dfs2(int u, int fa){

    if(u>fa){
        s[u]+=f[u]*(f[fa]+1);
        f[u]+=(f[fa]+1);
    }
    for(int i=0; i<v[u].size(); i++){
        int to=v[u][i];
        if(to!=fa){
            dfs2(to, u);
        }
    }
}

int main()
{
    //freopen("e1.in", "r", stdin);
    int t;scanf("%d", &t);
    while(t--){
        memset(s, 0, sizeof(s));
        memset(f, 0, sizeof(f));
        int n, u, to;
        scanf("%d", &n);
        for(int i=1; i<=n; i++){
            v[i].clear();
        }
        for(int i=1; i<=n-1; i++){
            scanf("%d%d", &u, &to);
            v[u].push_back(to);
            v[to].push_back(u);
        }
        dfs1(1, 0);
        dfs2(1, 1<<30);
        LL ans=0;
        for(int i=1; i<=n; i++){
            ans+=s[i];
        }
        printf("%lld\n", ans*2);
    }

    return 0;
}