[WC2010]重建计划 题解

[WC2010]重建计划 题解

Problem

​ 给定一棵树,边有边权,要求找到一条长度在\([L,R]\)之间的链,使得链的总价值和除以链长最大

Solution

​ 显然,这个式子的形式让人想到了01分数规划。于是根据01分数规划的套路,先二分一个答案

​ 考虑如何判断,现在等于把所有边都减去了一个\(mid\),要看有没有一条价值大于0的长度介于\([L,R]\)的链

​ 是不是感觉点分治呼之欲出。的确,到了这里就是一个点分治的经典例题了。但是这不是这篇题解的重点

​ 显然DP也可做,记\(f_{u,j}\)为在\(u\)子树中以\(u\)为端点的一条长度为\(j\)的链的最大价值,这个DP转移比较简单,但是复杂度是\(O(n^2)\)

​ 我们发现这个DP的第二维状态与深度有关,故可以用长链剖分优化,统计答案时也可以用线段树优化,于是复杂度优化到了\(O(nlog^2n)\)

Code

#include<bits/stdc++.h>
#define inf 1e8
#define DB double
#define eps 1e-3
using namespace std;
int n,l,r,cnt,tim;
DB ans,res,f[100005],g[100005];
int head[100005],w[200005],to[200005],Next[200005];
int dfn[100005],son[100005],dep[100005],sonw[100005];

struct Segment_Tree{

    DB seg[400005];

    void clear(){
        memset(seg,0xc2,sizeof seg);
    }

    void change(int k,int l,int r,int pos,DB val){
        seg[k]=max(seg[k],val);
        if(l==r)
            return;
        int mid=l+r>>1;
        if(pos<=mid)
            change(k<<1  ,l,mid  ,pos,val);
        else
            change(k<<1|1,mid+1,r,pos,val);
        return;
    }

    DB query(int k,int l,int r,int wl,int wr){
        if(wl> r||l> wr)
            return -inf;
        if(wl<=l&&r<=wr)
            return seg[k];
        DB res=-inf;
        int mid=l+r>>1;
        res=max(res,query(k<<1  ,l,mid  ,wl,wr));
        res=max(res,query(k<<1|1,mid+1,r,wl,wr));
        return res;
    }

}T;

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
       if(ch=='-')f=-1;
       ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
       x=(x<<1)+(x<<3)+ch-'0';
       ch=getchar();
    }
    return x*f;
}

inline void add(int u,int v,int p){
    w[++cnt]=p;to[cnt]=v;Next[cnt]=head[u];head[u]=cnt;
}

void dfs(int u,int fa){
    dep[u]=1;
    for(register int i=head[u];i;i=Next[i]){
        int v=to[i];
        if(v!=fa){
            dfs(v,u);
            dep[u]=max(dep[u],dep[v]+1);
            if(dep[v]>dep[son[u]]){
                son[u]=v;
                sonw[u]=w[i];
            }
        }
    }
    return;
}

void DP(int u,int fa,DB x){
    if(!dfn[u])
        dfn[u]=++tim;
    f[dfn[u]]=0;
    g[dfn[u]]=0;    
    if(son[u]){
        DP(son[u],u,x);
        f[dfn[u]]=x-g[dfn[u]+1]-sonw[u];
        g[dfn[u]]=g[dfn[u]+1]+sonw[u]-x;
    }
    T.change(1,1,n,dfn[u],f[dfn[u]]);
    for(register int i=head[u];i;i=Next[i]){
        int v=to[i];
        if(v==fa||v==son[u])
            continue;
        DP(v,u,x);
        for(register int j=1;j<=dep[v];++j){
            if(l<=dep[u]+j-1){
                double now=T.query(1,1,n,dfn[u]+max(1,l-j),dfn[u]+min(r-j,dep[u]-1));
                res=max(res,w[i]-x+f[dfn[v]+j-1]+g[dfn[v]]+g[dfn[u]]+now);
            }
        }
        for(register int j=1;j<=dep[v];++j){
            if(w[i]-x+f[dfn[v]+j-1]+g[dfn[v]]>g[dfn[u]]+f[dfn[u]+j]){
                f[dfn[u]+j]=w[i]-x+f[dfn[v]+j-1]+g[dfn[v]]-g[dfn[u]];
                T.change(1,1,n,dfn[u]+j,f[dfn[u]+j]);
            }
        }
    }
    if(dep[u]-1>=l)
        res=max(res,g[dfn[u]]+T.query(1,1,n,dfn[u]+l,dfn[u]+min(r,dep[u]-1)));
    return;
}

int check(DB x){
    res=-inf;
    T.clear();
    DP(1,0,x);
    return res>=0; 
}

int main(){
    
    n=read();l=read();r=read();

    for(register int i=2;i<=n;++i){
        int u=read(),v=read();
        int p=read();
        add(u,v,p);add(v,u,p);
    }

    dfs(1,0);

    DB L=0,R=1e6,ans=0;
    while(R-L>=eps/10){
        DB mid=(L+R)/2;
        if(check(mid)){
            ans=mid;
            L=mid;
        }
        else
            R=mid;
    }

    printf("%0.3lf\n",ans);

    return 0;
}
posted @ 2020-09-22 20:50  zjy123456  阅读(126)  评论(0编辑  收藏  举报