习题:Alternating Tree(树DP)

题目

传送门

思路

我们考虑即使是枚举路径也是\(n^2\)级别的复杂度

所以我们反过来考虑每一个点的贡献是什么

情况1

这个点直接连向儿子节点

此时的计算即为\(siz[u]*val[u]\)

情况2

考虑子树内的一条链跨过他到了子树内的另一点

这个时候也是比较好算的,我们只需要知道每一个点到他的时候,

这个点是-1还是1,这个用两个dp数组可以解决

情况3

考虑从子树外的一点经过他到子树内的一定

这种情况与情况二比较类似,dp基本上是一样的

代码

#include<iostream>
#include<vector>
using namespace std;
const int mod=1e9+7;
int n;
long long val[200005],siz[200005];
long long dp1[200005],f1[200005];//子树内奇数/偶数
long long dp2[200005],f2[200005];//子树外奇数/偶数
long long ans;
vector<int> g[200005];
void dfs1(int u,int fa)
{
    siz[u]=1;
    f1[u]=1;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v!=fa)
        {
            dfs1(v,u);
            dp1[u]+=f1[v];
            f1[u]+=dp1[v];
            siz[u]+=siz[v];
        }
    }
}
void dfs2(int u,int fa)
{
    long long s1=0,s2=0;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v!=fa)
        {
            s1+=dp1[v];
            s2+=f1[v];
        }
    }
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v!=fa)
        {
            dp2[v]=f2[u]+s1-dp1[v]+1;
            f2[v]=dp2[u]+s2-f1[v];
            dfs2(v,u);
        }
    }
}
void dfs3(int u,int fa)
{
    ans=(ans+(f1[u]-dp1[u])*val[u]%mod*(n-siz[u]+1)%mod)%mod;
    ans=(ans+(siz[u]-1)*val[u]%mod)%mod;
    ans=(ans+(f2[u]-dp2[u])*siz[u]%mod*val[u]%mod)%mod;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v!=fa)
        {
            ans=(ans+(dp1[v]-f1[v])*(siz[u]-siz[v]-1)%mod*val[u]%mod)%mod;
            dfs3(v,u);
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>val[i];
    for(int i=1,u,v;i<n;i++)
    {
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);    
    }
    dfs1(1,0);
    dfs2(1,0);
    dfs3(1,0);
    cout<<(ans+mod)%mod;
    return 0;
}
posted @ 2020-07-31 12:24  loney_s  阅读(151)  评论(0)    收藏  举报