ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

BZOJ1758 [Wc2010]重建计划

2019-02-21 09:00:27  阅读:308  来源: 互联网

标签:BZOJ1758 长链 ch return int Wc2010 mxd DP 重建


题意


N<=100000,1<=L<=U<=N-1,Vi<=1000000

分析

参照The_Virtuoso的题解。

这题算是长链剖分最经典的一道题了。

首先要找一个最优方案,直接DP显然不好做,那么考虑二分答案然后验证,因为是浮点数,要注意精度问题。

假设当前二分的答案是k,判断答案是否满足时原式也就转化成了\(\frac{\sum v_i}{|S|}\ge k\),将分母移到不等式右边得到\(\sum v_i \ge k∗|S|\)

将右边的部分移到左边就变成了\(\sum v_i−k∗|S| \ge 0\)

因为\(v_i\)的个数就是\(|S|\),因此将\(k*|S|\)放到\(Σ\)里面,判断就变成了\(\sum (v_i−k) \ge 0\)

每次判断只要把每条边的边权和减\(k\),再判断能否有一条路径边权和大于等于0就好了。

怎么判断呢?很容易想到\(O(n^2)\)dp,设f[i][j]表示i子树中与i距离为j的链的边权和最大值,枚举另一棵子树找到链长在[L-j,R-j]之内的边权最大值。

\(O(n^2)\)dp显然不行,但观察到dp是可合并的以深度为下标的转移方程,因此可以用长链剖分优化成\(O(n)\)。

怎么优化呢?

首先对整棵树长链剖分求出树剖序,再把树剖序架到线段树上,因为整棵树是由所有长链组成的,每条长链因为是优先遍历所以在树剖序上是连续的一段。

也就是说树剖序上的每一段都是树剖出的一条长链。那么每个点子树中每个深度的信息就可以都存到这个点往下的长链上。

当做树形DP时,每个点对于重儿子回溯时不做任何操作,直接继承;当轻儿子回溯时枚举轻儿子每个深度的边权和最大值,在长链上找到对应区间求最大值来更新答案。

然后再把这个轻儿子的信息合并到长链上。因为长链上存的是之前所有遍历过的子树合并后的信息,所以相当于每个点子树中有用的信息都在这个点往下的长链上。最后别忘了考虑从上到下的每条直链。

这样DP是\(O(n)\)的,再加上线段树的\(O(log)\)和二分的\(O(log)\)一共是\(O(n \log^2n)\)。

至于这样DP为什么是\(O(n)\)的?

因为每个点对重儿子是直接继承的,而每个点需要被DP当且仅当它是轻儿子时,这时它一定是一个长链的链头,DP的时间复杂度是这个点往下的长链长度,那么DP的总复杂度就是每条长链的链长总和,也就是\(O(n)\)。

代码

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;
    rg char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x){
    return x=read<T>();
}
typedef long long ll;

co int N=1e5+1;
co double INF=1e11,eps=1e-4;
int n,L,R;
int head[N],to[N*2],v[N*2],nx[N*2],tot;
int fa[N],dep[N],mxd[N],son[N],pos[N],dfn;
void add(int x,int y,int w){
    to[++tot]=y,v[tot]=w,nx[tot]=head[x],head[x]=tot;
}
void dfs1(int x){
    dep[x]=dep[fa[x]]+1,mxd[x]=dep[x];
    for(int i=head[x];i;i=nx[i]){
        if(to[i]==fa[x]) continue;
        fa[to[i]]=x;
        dfs1(to[i]);
        mxd[x]=std::max(mxd[x],mxd[to[i]]);
        if(mxd[to[i]]>mxd[son[x]]) son[x]=to[i];
    }
}
void dfs2(int x){
    pos[x]=++dfn;
    if(!son[x]) return;
    dfs2(son[x]);
    for(int i=head[x];i;i=nx[i]){
        if(to[i]==fa[x]||to[i]==son[x]) continue;
        dfs2(to[i]);
    }
}
int id[N];
double ans,dis[N],tmp[N],val[N*2],mxv[N*4];
void build(int x,int l,int r){
    mxv[x]=-INF;
    if(l==r){
        id[l]=x;
        return;
    }
    int mid=(l+r)>>1;
    build(x<<1,l,mid),build(x<<1|1,mid+1,r);
}
void change(int x,int l,int r,int p,double v){
    mxv[x]=std::max(mxv[x],v);
    if(l==r) return;
    int mid=(l+r)>>1;
    if(p<=mid) change(x<<1,l,mid,p,v);
    else change(x<<1|1,mid+1,r,p,v);
}
double query(int x,int l,int r,int ql,int qr){
    if(ql>qr) return -INF;
    if(ql<=l&&r<=qr) return mxv[x];
    int mid=(l+r)>>1;
    if(qr<=mid) return query(x<<1,l,mid,ql,qr);
    if(ql>mid) return query(x<<1|1,mid+1,r,ql,qr);
    return std::max(query(x<<1,l,mid,ql,qr),query(x<<1|1,mid+1,r,ql,qr));
}
void tree_dp(int x){
    change(1,1,n,pos[x],dis[x]);
    for(int i=head[x];i;i=nx[i]) if(son[x]==to[i]){
        dis[son[x]]=dis[x]+val[i];
        tree_dp(son[x]);
        break;
    }
    for(int i=head[x];i;i=nx[i]){
        if(to[i]==fa[x]||to[i]==son[x]) continue;
        dis[to[i]]=dis[x]+val[i];
        tree_dp(to[i]);
        for(int j=1;j<=mxd[to[i]]-dep[x];++j){
            tmp[j]=mxv[id[pos[to[i]]+j-1]];
            if(j<=R) ans=std::max(ans,query(1,1,n,std::max(pos[x],pos[x]+L-j),std::min(pos[x]+mxd[x]-dep[x],pos[x]+R-j))+tmp[j]-2*dis[x]);
        }
        for(int j=1;j<=mxd[to[i]]-dep[x];++j)
            change(1,1,n,pos[x]+j,tmp[j]);
    }
    ans=std::max(ans,query(1,1,n,pos[x]+L,std::min(pos[x]+mxd[x]-dep[x],pos[x]+R))-dis[x]);
}
int main(){
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    read(n),read(L),read(R);
    for(int i=1;i<n;++i){
        int x=read<int>(),y=read<int>(),w=read<int>();
        add(x,y,w),add(y,x,w);
    }
    dfs1(1),dfs2(1);
    double l=0,r=1e6;
    while(r-l>eps){
        double mid=(l+r)/2;
        for(int i=1;i<=tot;++i)
            val[i]=v[i]-mid;
        build(1,1,n);
        ans=-INF,dis[1]=0;
        tree_dp(1);
        if(ans<0) r=mid;
        else l=mid;
    }
    printf("%.3lf",l);
    return 0;
}

标签:BZOJ1758,长链,ch,return,int,Wc2010,mxd,DP,重建
来源: https://www.cnblogs.com/autoint/p/10410335.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有