[bzoj1758][Wc2010]重建计划——长链剖分+线段树+分数规划

题目大意:

给定一颗带权的树,求一条长路在 [ L , R ] [L,R] [L,R]的路径,权值的平均数最大。

思路:

显然先分数规划,二分答案,然后考虑怎么check。
考虑一个简单的树型DP,记 f i , j f_{i,j} fi,j为i子树内距离i为j的点中路径长度和最大是多少,然后一个点可以从它的儿子转移过来,在转移的时候每次记录前缀枚举新添加进来的子树深度计算一遍答案。
这样复杂度 O ( n 2 ) O(n^2) O(n2),发现转移和子树的深度有关,然后第一颗转移的子树只是涉及到了下标的改变,于是考虑长链剖分优化复杂度,每一次直接先找到重儿子继承,然后再枚举每一条轻边,暴力转移每一条轻边上面的链。
由于有长度限制,考虑将每一条长链放到线段树上维护,查询的时候直接在线段树上面查询区间最大值即可。
有的打法需要支持线段树的区间修改,但其实没有这个必要,一个trick就是将 f i , j f_{i,j} fi,j表示为距离i深度为j的点距离根的最长路径长度,计算时再考虑lca的贡献即可。

#include<bits/stdc++.h>
 
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
 
using namespace std;
 
void File(){
    freopen("bzoj1758.in","r",stdin);
    freopen("bzoj1758.out","w",stdout);
}
 
template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}
 
const int maxn=1e5+10;
const double inf=1e18;
int n,Lbound,Rbound,s[maxn],sp;
int beg[maxn],to[maxn<<1],las[maxn<<1],cnte=1;
double w[maxn<<1],val[maxn];
int son[maxn],len[maxn],fa[maxn],dfn[maxn],cnt_dfn;
 
void add(int u,int v,int ww){
    las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; w[cnte]=ww;
    las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u; w[cnte]=ww;
}
 
void dfs1(int u,int fh){
    fa[u]=fh;
    for(int i=beg[u];i;i=las[i]){
        int v=to[i];
        if(v==fh)continue;
        dfs1(v,u);
        if(len[v]+1>len[u]){
            len[u]=len[v]+1;
            son[u]=v;
            val[u]=w[i];
        }
    }
}
 
void dfs2(int u){
    dfn[u]=++cnt_dfn;
    if(son[u])dfs2(son[u]);
    for(int i=beg[u];i;i=las[i]){
        int v=to[i];
        if(v==fa[u] || v==son[u])continue;
        dfs2(v);
    }
}
 
struct Segment_Tree{
#define mid ((l+r)>>1)
#define lc (o<<1)
#define rc (o<<1|1)
#define lson lc,l,mid
#define rson rc,mid+1,r
    double mx[maxn<<2];
    void build(int o,int l,int r){
        mx[o]=-inf;
        if(l==r)return;
        build(lson),build(rson);
    }
    void update(int o,int l,int r,int p,double x){
        if(l==r)mx[o]=max(mx[o],x);
        else{
            if(p<=mid)update(lson,p,x);
            else update(rson,p,x);
            mx[o]=max(mx[lc],mx[rc]);
        }
    }
    double query(int o,int l,int r,int L,int R){
        if(L>R)return -inf;
        if(L<=l && r<=R)return mx[o];
        else{
            double ret=-inf;
            if(L<=mid)ret=max(ret,query(lson,L,R));
            if(R>=mid+1)ret=max(ret,query(rson,L,R));
            return ret;
        }
    }
#undef mid
}T;
 
bool flag;
 
void solve(int u,double sum,double x){
    if(flag)return;
    T.update(1,1,n,dfn[u],sum);
    if(son[u])solve(son[u],sum+val[u]-x,x);
    for(int i=beg[u];i;i=las[i]){
        int v=to[i];
        if(v==fa[u] || v==son[u])continue;
        solve(v,sum+w[i]-x,x);
        if(flag)return;
        REP(j,dfn[v],dfn[v]+len[v]){
            int dis=j-dfn[v]+1,l=Lbound-dis+dfn[u],r=Rbound-dis+dfn[u];
            l=max(l,dfn[u]),r=min(r,dfn[u]+len[u]);
            if(T.query(1,1,n,j,j)+T.query(1,1,n,l,r)-sum*2>0)flag=true;
            if(flag)return;
        }
        REP(j,dfn[v],dfn[v]+len[v]){
            int p=j-dfn[v]+1+dfn[u];
            T.update(1,1,n,p,T.query(1,1,n,j,j));
        }
    }
    int l=dfn[u]+Lbound,r=min(dfn[u]+len[u],dfn[u]+Rbound);
    if(T.query(1,1,n,l,r)-sum>0)flag=true;
}
 
bool judge(double x){
    T.build(1,1,n);
    flag=0;
    solve(1,0,x);
    return flag;
}
 
int main(){
    //File();
    read(n),read(Lbound),read(Rbound);
    int u,v,ww;
    REP(i,1,n-1)read(u),read(v),read(ww),add(u,v,ww);
    dfs1(1,0);
    dfs2(1);
    double l=0,r=1e6;
    while(r-l>1e-4){
        double mid=(l+r)/2;
        if(judge(mid))l=mid;
        else r=mid;
    }
    printf("%.3lf\n",l);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: bzoj作为一个计算机竞赛的在线评测系统,不仅可以提供大量的题目供程序员练习和学习,还可以帮助程序员提升算法和编程能力。为了更好地利用bzoj进行题目的学习和刷题,制定一个bzoj做题计划是非常有必要的。 首先,我们需要合理安排时间,每天留出一定的时间来做bzoj的题目。可以根据自己的时间安排,每天挑选适量的题目进行解答。可以先从难度较低的题目开始,逐渐提高难度,这样既能巩固基础知识,又能挑战自己的思维能力。 其次,要有一个计划和目标。可以规划一个每周或每月的题目数量目标,以及每个阶段要学习和掌握的算法知识点。可以根据bzoj的题目分类,如动态规划、图论、贪心算法等,结合自己的实际情况,有针对性地选择题目进行学习。 此外,要充分利用bzoj提供的资源。bzoj网站上有很多高质量的题解和优秀的解题代码,可以参考和学习。还有相关的讨论区,可以与其他程序员交流和讨论,共同进步。 最后,要坚持并保持思考。做题不是单纯为了刷数量,更重要的是学会思考和总结。遇到难题时,要有耐心,多思考,多尝试不同的解法。即使不能一次性解出来,也要学会思考和分析解题过程,以及可能出现的错误和优化。 总之,bzoj做题计划的关键在于合理安排时间、制定目标、利用资源、坚持思考。通过有计划的刷题,可以提高算法和编程能力,并培养解决问题的思维习惯,在计算机竞赛中取得更好的成绩。 ### 回答2: bzoj做题计划是指在bzoj这个在线测评系统上制定一套学习和刷题的计划,并且将计划记录在excel表格中。该计划主要包括以下几个方面的内容。 首先是学习目标的设定。通过分析自己的水平和知识缺口,可以设定一个合理的目标,比如每天解决一定数量的题目或者提高特定的算法掌握程度。 其次是题目选择的策略。在excel表格中可以记录下自己选择的题目编号、题目类型和难度等信息。可以根据题目的类型和难度来安排每天的刷题计划,确保自己可以逐步提高技巧和解题能力。 然后是学习进度的记录和管理。将每天的完成情况记录在excel表格中,可以清晰地看到自己的学习进度和任务完成情况。可以使用图表等功能来对学习进度进行可视化展示,更好地管理自己的学习计划。 同时,可以在excel表格的备注栏中记录下每道题目的解题思路、关键点和需要复习的知识点等信息。这样可以方便自己回顾和总结,巩固所学的知识。 最后,可以将excel表格与其他相关资料进行整合,比如算法教材、题目解析和学习笔记等。这样可以形成一个完整的学习档案,方便自己进行系统的学习和复习。 总之,bzoj做题计划excel的制定和记录可以帮助我们更加有条理和高效地进行学习和刷题。通过合理安排学习目标和题目选择策略,记录学习进度和思路,并整合其他学习资料,我们可以提高自己的解题能力,并在bzoj上取得更好的成绩。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值