bzoj1758: [Wc2010]重建计划(01分数规划+长链剖分+线段树)

传送门
长链剖分好题。
题意简述:给一棵树,问边数在 [ L , R ] [L,R] [L,R]之间的路径权值和与边数之比的最大值。


思路:
用脚指头想都知道要01分数规划。
考虑怎么 c h e c k check check
发现就是求在转化成真·边权之后有没有长度在 [ L , R ] [L,R] [L,R]之间的路径权值是大于0的。
然后可以设计状态 f i , j f_{i,j} fi,j表示 i i i开头长度为 j j j的路径最大值,这个可以用长链剖分优化转移。
然后考虑怎么把经过 i i i的两条路径拼起来更新答案,这个可以用线段树优化转移,然后做完了。
代码:

#include
#define fi first
#define se second
#define ri register int
using namespace std;
inline int read(){
    int ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
const double eps=1e-4,inf=1e18;
const int N=1e5+5;
int n,len[N],fa[N],hson[N],tot=0,mxdep[N],dep[N],pos[N],num[N],D,U;
typedef pair<int,double> pii;
vector<pii>e[N];
struct edge{int u,v,w;}G[N];
double ans,f[N];
struct segement_tree{
    #define lc (p<<1)
    #define rc (p<<1|1)
    #define mid (l+r>>1)
    double mx[N<<2];
    inline void pushup(int p){mx[p]=max(mx[lc],mx[rc]);}
    inline void build(int p,int l,int r){
        mx[p]=-inf;
        if(l==r){pos[l]=p;return;}
        build(lc,l,mid),build(rc,mid+1,r);
    }
    inline void update(int p,int l,int r,int k,double v){
        if(l==r){mx[p]=max(mx[p],v);return;}
        k<=mid?update(lc,l,mid,k,v):update(rc,mid+1,r,k,v),pushup(p);
    }
    inline double query(int p,int l,int r,int ql,int qr){
        if(ql>r||qr<l)return -inf;
        if(ql<=l&&r<=qr)return mx[p];
        if(qr<=mid)return query(lc,l,mid,ql,qr);
        if(ql>mid)return query(rc,mid+1,r,ql,qr);
        return max(query(lc,l,mid,ql,mid),query(rc,mid+1,r,mid+1,qr));
    }
}T;
void dfs1(int p){
    for(ri i=0,v;i<e[p].size();++i){
        if((v=e[p][i].fi)==fa[p])continue;
        fa[v]=p,dep[v]=mxdep[v]=dep[p]+1,dfs1(v),mxdep[p]=max(mxdep[p],mxdep[v]);
        if(mxdep[p]==mxdep[v])hson[p]=v;
    }
    len[p]=mxdep[p]-dep[p];
}
void dfs2(int p){
    num[p]=++tot;
    if(!hson[p])return;
    dfs2(hson[p]);
    for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i].fi)!=fa[p]&&v!=hson[p])dfs2(v);
}
void solve(int p,double dist){
    T.update(1,1,n,num[p],dist);
    for(ri i=0;i<e[p].size();++i)if(e[p][i].fi==hson[p]){solve(hson[p],dist+e[p][i].se);break;}
    for(ri i=0,v;i<e[p].size();++i){
        if((v=e[p][i].fi)==fa[p]||v==hson[p])continue;
        solve(v,dist+e[p][i].se);
        for(ri j=1;j<=mxdep[v]-dep[p];++j){
            f[j]=T.mx[pos[num[v]+j-1]];
            if(j<=U){
                double tmp=T.query(1,1,n,max(1,num[p]+D-j),min(num[p]+U-j,num[p]+len[p]));
                ans=max(ans,tmp+f[j]-2*dist);
            }
        }
        for(ri j=1;j<=mxdep[v]-dep[p];++j)T.update(1,1,n,num[p]+j,f[j]);
    }
    ans=max(ans,T.query(1,1,n,num[p]+D,min(num[p]+U,num[p]+len[p]))-dist);
}
inline bool check(double tmp){
    ans=-inf,T.build(1,1,n);
    for(ri i=1;i<=n;++i)e[i].clear();
    for(ri i=1,u,v;i<n;++i){
        u=G[i].u,v=G[i].v;
        double w=G[i].w-tmp;
        e[u].push_back(pii(v,w)),e[v].push_back(pii(u,w));
    }
    return solve(1,0),ans>=-eps;
}
int main(){
    n=read(),D=read(),U=read();
    double L=inf,R=0;
    for(ri i=1;i<n;++i){
        G[i].u=read(),G[i].v=read(),G[i].w=read(),L=min(L,(double)G[i].w),R=max(R,(double)G[i].w);
        e[G[i].u].push_back(pii(G[i].v,0)),e[G[i].v].push_back(pii(G[i].u,0));
    }
    dfs1(1),dfs2(1);
    while(R-L>=eps){
        double Mid=(L+R)/2;
        if(check(Mid))L=Mid;
        else R=Mid;
    }
    printf("%.3lf",L);
    return 0;
}

你可能感兴趣的:(#,线段树,#,01分数规划,#,长链剖分)