#4709. 树

题目描述

题解

考虑一条路径 $(u,v)$ 的影响,那对 $lca$ 及其祖先的贡献都是 $(d_u-d_lca)(d_v-d_lca)$ ,对 $(u,v)$ 这条路径上的点,每个点和其子树内的贡献是一样的,列式子发现是等差数列的形式,于是我们可以做树上差分,具体来说我们发现对于 $(u,lca)$ 这条路径上的点 $x$ , $\Delta a_x-\Delta a_{son}=dis_{u,v}+1-2(dp_x-dp_u)$ ,于是我们可以在 $u$ 或 $v$ 上打上首项和公差的标记,在 $lca$ 上打上末项和公差的负标记,每次首项都加上公差即可

效率: $O(nlogn)$ (需要倍增)

代码

#include <bits/stdc++.h>
using namespace std;
const int N=3e5+5,M=N<<1;
int n,m,hd[N],V[M],nx[M],t,dp[N],fa[N][20];
long long a[N],b[N],s[N];
void add(int u,int v){
    nx[++t]=hd[u];V[hd[u]=t]=v;
}
void dfs1(int u,int fr){
    dp[u]=dp[fa[u][0]=fr]+1;
    for (int i=1;fa[fa[u][i-1]][i-1];i++)
        fa[u][i]=fa[fa[u][i-1]][i-1];
    for (int i=hd[u];i;i=nx[i])
        if (V[i]!=fr) dfs1(V[i],u);
}
int lca(int u,int v){
    if (dp[u]<dp[v]) swap(u,v);
    for (int i=19;~i;i--)
        if (dp[fa[u][i]]>=dp[v]) u=fa[u][i];
    if (u==v) return u;
    for (int i=19;~i;i--)
        if (fa[u][i]!=fa[v][i])
            u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}
void dfs2(int u,int fr){
    for (int v,i=hd[u];i;i=nx[i])
        if ((v=V[i])!=fr)
            dfs2(v,u),a[u]+=a[v],b[u]+=b[v];
    a[u]-=b[u];
}
void dfs3(int u,int fr){
    for (int v,i=hd[u];i;i=nx[i])
        if ((v=V[i])!=fr)
            s[v]=s[u]-a[v],dfs3(v,u);
}
int main(){
    cin>>n>>m;
    for (int i=1,u,v;i<n;i++)
        scanf("%d%d",&u,&v),
        add(u,v),add(v,u);
    dfs1(1,0);
    for (int u,v,z,l;m--;){
        scanf("%d%d",&u,&v);
        z=lca(u,v);l=dp[u]+dp[v]-(dp[z]<<1);
        a[u]+=l+1;a[v]+=l+1;a[z]-=2;
        b[u]+=2;b[v]+=2;b[z]-=4;
        s[1]+=1ll*(dp[u]-dp[z])*(dp[v]-dp[z]);
    }
    dfs2(1,0);dfs3(1,0);
    for (int i=1;i<=n;i++)
        printf("%lld\n",s[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xjqxjq/p/12283450.html