【JZOJ 4923】 巧克力狂欢 树的直径经典问题

来源:互联网 发布:软件开发招标文件 编辑:程序博客网 时间:2024/04/30 12:38

Description

给你一棵树,每个点有权,求出树中两条不相交的路径,使得两个路径上的点权总和最大
输出这个最大值
对于100%的数据,n<=200000,0<=ai<=1000000000(1e9)

Analysis

这道题有多种方法,可以DP,用f[i][0/1]来乱搞
这是一个神奇的根据直径性质的方法
默认直径是横着放的
首先求出直径,再求出不与直径相交的最长路径,求和作为答案
求出直径上每个点左右两侧的最长路径(设为l[v],r[v]):注意这个最长路径可能是延伸到直径该侧端点,也可能延伸到这个点某个子树下面
这是可以O(n)求的
然后在直径上随机选u,v(u在v左边),用l[u]+r[v]更新答案
求前后缀最大值就能做到O(n)了

Code

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,b,a) for(int i=b;i>=a;i--)#define efo(i,v) for(int i=last[v];i;i=next[i])using namespace std;typedef long long ll;const int N=200010,M=N*2;int n,m,tot,u1,u2,v1,v2,d[N];ll mx,ans,a[N],b[N],c[N],sl[N],sr[N],l[N],r[N],ml[N],mr[N];int fa[N],f[N],g[N],to[M],next[M],last[N];bool tag,bz[N];void link(int u,int v){    to[++tot]=v,next[tot]=last[u],last[u]=tot;}void dfs1(int v,int fr,ll k){    if(k>mx) mx=k,v1=v;    efo(i,v)    {        int u=to[i];        if(u==fr || bz[u]) continue;        dfs1(u,v,k+a[u]);    }}void dfs2(int v,int fr,ll k){    if(k>mx) mx=k,v2=v;    if(!tag) fa[v]=fr;    efo(i,v)    {        int u=to[i];        if(u==fr || bz[u]) continue;        dfs2(u,v,k+a[u]);    }}void get(int i){    mx=0;dfs1(i,i,a[i]);    mx=0;dfs2(v1,v1,a[v1]);    ll t1=0,t2=0;    for(int v=v1;;)    {        t1+=a[v];        if(v==i) break;        v=fa[v];    }    for(int v=v2;;)    {        t2+=a[v];        if(v==i) break;        v=fa[v];    }    b[i]=max(t1,t2);    if(v1==v2) c[i]=t1;    else c[i]=t1+t2-a[i];}int main(){    int u,v;    scanf("%d",&n);    fo(i,1,n) scanf("%d",&a[i]);    fo(i,1,n-1)    {        scanf("%d %d",&u,&v);        link(u,v),link(v,u);    }    dfs1(1,1,a[1]);mx=0;    dfs2(v1,v1,a[v1]);    u1=v1,u2=v2;    ll disu=0,disv=0;    for(int u=u2;;)    {        bz[u]=1,disu+=a[u];        d[++m]=u;        if(u==u1) break;        u=fa[u];    }    tag=1;    fo(k,1,m)    {        int u=d[k];        efo(j,u)        {            int i=to[j];            if(bz[i]) continue;            get(i);            disv=max(disv,c[i]);        }        get(u);    }    ans=disv+disu;    fo(k,1,m)    {        int u=d[k];        sl[k]=sl[k-1]+a[d[k]];        l[k]=max(c[u],b[u]+sl[k-1]);        ml[k]=max(ml[k-1],l[k]);    }    fd(k,m,1)    {        int u=d[k];        sr[k]=sr[k+1]+a[d[k]];        r[k]=max(c[u],b[u]+sr[k+1]);        mr[k]=max(mr[k+1],r[k]);        ans=max(ans,mr[k]+ml[k-1]);    }    printf("%lld",ans);    return 0;}
0 0