G.小红的树上路径查询(hard)

知识点:树上前缀和,最近公共祖先,换根dp

首先可以利用换根dp,求dp[u]表示,以u为根节点,其余所有节点到点u的总距离和

显然路径由两段构成

推导得一条链上的其中一部分贡献和(除去lca(u,v)节点父亲的贡献)为:

pre[u]:根节点到点u经过的所有点的子树大小的和

sum[u]:点u的子树中的所有节点到u的距离和

计算lca(u,v)节点父亲所带来的贡献(类似处理换根dp的思路):

化简:

最终的ans即为两部分的贡献和

code:

void solve(int T)
{
    int n,q;
    cin>>n>>q;
    vector<vector<int>>e(n+1);
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    vector f(n+1,vector<int>(20));
    vector<int>dp(n+1),son(n+1),dep(n+1),pre(n+1),fa(n+1),sum(n+1);
    auto dfs1=[&](auto &&dfs1,int u,int p)->void
    {
        son[u]=1,f[u][0]=p,dep[u]=dep[p]+1,fa[u]=p;
        for(int i=1;i<=19;i++)f[u][i]=f[f[u][i-1]][i-1];
        for(auto i:e[u])
        {
            if(i!=p)
            {
                dfs1(dfs1,i,u);
                sum[u]+=(sum[i]+son[i]);
                son[u]+=son[i];
            }
        }
    };
    auto dfs2=[&](auto &&dfs2,int u,int p)->void
    {
        for(auto i:e[u])
        {
            if(i!=p)
            {
                dp[i]=dp[u]-son[i]+n-son[i];
                pre[i]=pre[u]+son[i];
                dfs2(dfs2,i,u);
            }
        }
    };
    auto lca=[&](int u,int v)->int
    {
        if(dep[u]<dep[v])swap(u,v);
        for(int i=19;i>=0;i--)if(dep[f[u][i]]>=dep[v])u=f[u][i];
        if(u==v)return v;
        for(int i=19;i>=0;i--)if(f[u][i]!=f[v][i])u=f[u][i],v=f[v][i];
        return f[u][0];
    };
    dfs1(dfs1,1,0);
    dp[1]=sum[1];
    pre[1]=son[1];
    dfs2(dfs2,1,0);
    for(int i=1;i<=q;i++)
    {
        int u,v;
        cin>>u>>v;
        if(dep[u]>dep[v])swap(u,v);//让u在上
        if(u==v)cout<<dp[u]<<endl;
        else
        {
            int p=lca(u,v),ans=0;
            if(p==u)
            {
                ans=sum[p]-(pre[v]-pre[p]);
                if(fa[u])ans+=dp[fa[u]]-sum[u]-son[u]+n-son[u];
                cout<<ans<<endl;
            }
            else
            {
                if(fa[p])ans+=dp[fa[p]]-sum[p]-son[p]+n-son[p];
                ans+=sum[p]-(pre[v]-pre[p])-(pre[u]-pre[p]);
                cout<<ans<<endl;
            }
        }
    }
}
全部评论

相关推荐

06-11 17:39
门头沟学院 Java
小呆呆的大鼻涕:卧槽,用户彻底怒了
点赞 评论 收藏
分享
昨天 15:02
门头沟学院 Java
刚打开网申页面就不想填了,还是不要为难自己了
poppinzhan...:多益老行业毒瘤了,碰到徐波这种恶心的烂人,去了也是受罪。
点赞 评论 收藏
分享
评论
2
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务