bzoj1758Wc10重建计划——solution

1758: [Wc2010]重建计划

Time Limit: 40 Sec  Memory Limit: 162 MB
Submit: 4707  Solved: 1200
[Submit][Status][Discuss]

Description

Input

第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai,Bi,Vi分别表示道路(Ai,Bi),其价值为Vi 其中城市由1..N进行标号

Output

输出最大平均估值,保留三位小数

Sample Input

4
2 3
1 2 1
1 3 2
1 4 3

Sample Output

2.500

HINT

 

N<=100000,1<=L<=U<=N-1,Vi<=1000000 新加数据一组 By leoly,但未重测..2016.9.27

 
        -by bzoj
https://www.lydsy.com/JudgeOnline/problem.php?id=1758


省选R1虽有些不足之处(D2T1写炸了,还被卡了一个点的常数),不过基于我那点NOIP分,能苟到这个排名就不错了,知足吧~~,(SD一直到前50之前都没有NOIP比我低的......)
于是就来准备R2啦~~
被大佬安利了一发长链剖分
于是就上网找BZOJ的长链剖分题
于是就找到了这个
一看就是分数规划嘛;
套用分数规划的常用二分解法,考虑怎么check,
check需要找条最长链,如果没有[L,U]的限制,可以直接DP最长链(最近怎么光见到这个)
然而有了这个限制DP大概要N^3,(有N^2做法??N^2log??)
所以这个限制怎么办呢?
点分加线段树应该可以做(nlog^3)
(点分加单调队列应该可以nlog^2??)
不过今天要用长链剖分
考虑在每个点X上,维护子树中所有点从根出发到这个点的路径,这样可以通过N^2枚举每两条(深度加起来-二倍X深度)符合范围的路径来各种作差更新答案
N^2枚举所有点,可以改成N^2枚举所有深度,然后可以用一个下标为深度的线段树变成NlogN——因为当两个深度中有一个确定后,另一个的范围是连续的一段区间
然而这个效率总共是N^2log的,也十分不好的
而且内存也开不下,而且维护这个线段树的复杂度也十分不对
后两个问题可以考虑用线段树合并解决,
每个点只有一个深度,所以线段树合并可以做到维护所有点X的时空复杂度为(NlogN)
剩下的问题是NlogN枚举深度进行N次会变为N^2logN
这个怎么办呢?
注意我们对每个X的子树的枚举过程:
——把前i-1个儿子的子树和X合并,然后枚举第i个儿子的子树中的所有深度在X的线段树中查找
这样可以在不遗漏的前提下尽可能少查询
但是,对i=1时不应该这么做,
因为即使枚举第一个儿子的所有深度,所能查到的另一个深度也只有x自己的那一个深度,
所以在i=1时,可以考虑在第一个儿子的线段树中查询一个可以和X自己的深度匹配的区间
但是这样虽然常数可能小一点,但效率还是NlogN的
然而,我们发现,实际上对于每个X而言,都有第一个儿子的对应信息没有枚举!!
理所当然地,我们希望第一个儿子是最大深度最大的那个儿子,这样可以更快些;
可是,对于复杂度而言,这有什么用吗?
其实这对于复杂度而言十分有用:
这时,我们考虑在枚举所有X的子树的过程中,每个点对效率的影响
每个点只会在枚举其祖先的子树时有可能影响效率
我们把这棵树按照子树的最大深度而不是子树大小来剖分

 

如上图所示
这时我们发现,每个点a只会在枚举到他所在的重链的顶端的父亲时对效率造成log的影响

因为:

在枚举a所在的重链中的其他祖先节点时,a所在的子树都是作为深度最深的那个而没有被枚举(如枚举b的子树时,a所在的子树是最深的,没有被枚举深度)

在枚举更靠上的重链内部时,a所在的子树也是作为深度最深的那个而没有被枚举(如枚举e的子树时,a在d这个子树内,作为最深的存在,没有被枚举深度)

在枚举更靠上的重链顶端的父亲时,虽然a所在的子树需要枚举,但由于我们枚举的是深度,所以因为这个子树有更深的链所以这个效率应该算作那个更深的链的效率(如当枚举d的子树时,尽管a所在的子树被枚举了深度,但这个效率应该被算在cf链上)

所以这个方法可以做到nlogn

这个“按子树最大深度剖分树链,对长链链接的子节点不做操作”的技巧被称作一种长链剖分

于是我们完美地用$Nlog_2^2N$解决了这个问题

(upd 2018.5.24:不采用线段树合并,转而采用基于长链剖分的暴力线段树插入,好像也可以保证效率)

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const LL exp=10000;
const LL INF=1e15;
struct ss{
    int to,next;
    LL val;
}e[200010];
int first[100010],num;
struct DT{
    LL max;
    int ch[2];
}data[3000010];
int root[100010],tot;
int dep[100010],depst[100010],hw[100010];
LL val[100010];
LL l,r,mid,ans;
int n,Low,Top;
void build(int ,int ,LL );
bool check(LL );
void dfs_1(int ,int );
void dfs_2(int );
void insert(int ,int ,int&,int ,LL );
LL get_max(int ,int ,int ,int ,int );
LL get_poi_max(int ,int ,int ,int );
void merge(int ,int ,int ,int&);
int main()
{
    int i,j,k,o;
    scanf("%d",&n);
    scanf("%d%d",&Low,&Top);
    for(i=1;i<n;i++){
        scanf("%d%d%d",&j,&k,&o);
        build(j,k,o*exp),build(k,j,o*exp);
    }
    dfs_1(1,0);
    l=exp,r=1e10,mid=(l+r)>>1ll;
    while(l<r-3){
        if(check(mid))    l=mid;
        else            r=mid-1;
        mid=(l+r)>>1ll;
    }
    for(mid=r;mid>=l;mid--)
        if(check(mid)){
            printf("%.3lf",mid/10000.0);
            return 0;
        }
}
void build(int f,int t,LL v){
    e[++num].next=first[f];
    e[num].to=t,e[num].val=v;
    first[f]=num;
}
bool check(LL lim){
    int i;
    memset(root,0,sizeof(root)),tot=0;
    memset(data,0,sizeof(data)),data[0].max=-3*INF;
    for(i=1;i<=num;i++)    e[i].val-=lim;
    ans=-3e10;
    dfs_2(1);
    for(i=1;i<=num;i++)    e[i].val+=lim;
    return ans>=0;
}
void dfs_1(int now,int fa){
    int i;
    dep[now]=dep[fa]+1;
    depst[now]=dep[now],hw[now]=-1;
    for(i=first[now];i;i=e[i].next)
        if(e[i].to!=fa){
            dfs_1(e[i].to,now);
            if(depst[now]<depst[e[i].to])
                depst[now]=depst[e[i].to],hw[now]=i;
        }
}
void dfs_2(int now){
    int i,j;
    if(hw[now]!=-1){
        val[e[hw[now]].to]=val[now]+e[hw[now]].val;
        dfs_2(e[hw[now]].to);
        root[now]=root[e[hw[now]].to];
        if(dep[now]+Low<=depst[now]);
            ans=max(ans,get_max(1,n,root[now],dep[now]+Low,min(depst[now],dep[now]+Top))-val[now]);
        insert(1,n,root[now],dep[now],val[now]);
    }
    else{
        insert(1,n,root[now],dep[now],val[now]);
        return ;
    }
    for(i=first[now];i;i=e[i].next)
        if(dep[e[i].to]>dep[now]&&i!=hw[now]){
            val[e[i].to]=val[now]+e[i].val;
            dfs_2(e[i].to);
            for(j=dep[e[i].to];j<=depst[e[i].to]&&j<=dep[now]+Top;j++)
                ans=max(ans,get_poi_max(1,n,root[e[i].to],j)-val[now]+get_max(1,n,root[now],max(dep[now]*2+Low-j,dep[now]+1),min(dep[now]*2+Top-j,depst[now]))-val[now]);
            merge(1,n,root[e[i].to],root[now]);
        }
}
void insert(int l,int r,int&now,int lim,LL x){
    if(!now)now=++tot;
    if(l==r){
        data[now].max=x;
        return ;
    }
    int mid=(l+r)>>1;
    if(lim<=mid)
        insert(l,mid,data[now].ch[0],lim,x);
    else
        insert(mid+1,r,data[now].ch[1],lim,x);
    data[now].max=max(data[data[now].ch[0]].max,data[data[now].ch[1]].max);
}
LL get_max(int l,int r,int now,int L,int R){
    if(L>R)return data[0].max;
    if(L<=l&&r<=R)
        return data[now].max;
    int mid=(l+r)>>1;
    LL lm=-INF,rm=-INF;
    if(L<=mid)
        lm=get_max(l,mid,data[now].ch[0],L,R);
    if(R>mid)
        rm=get_max(mid+1,r,data[now].ch[1],L,R);
    if(lm>rm)    return lm;
    return rm;
}
LL get_poi_max(int l,int r,int now,int lim){
    if(l==r)return data[now].max;
    int mid=(l+r)>>1;
    if(lim<=mid)
        return get_poi_max(l,mid,data[now].ch[0],lim);
    else
        return get_poi_max(mid+1,r,data[now].ch[1],lim);
}
void merge(int l,int r,int pre,int&now){
    if(!now||!pre){
        now+=pre;
        return ;
    }
    if(l==r){
        data[now].max=max(data[now].max,data[pre].max);
        return ;
    }
    int mid=(l+r)>>1;
    merge(l,mid,data[pre].ch[0],data[now].ch[0]);
    merge(mid+1,r,data[pre].ch[1],data[now].ch[1]);
    data[now].max=max(data[data[now].ch[0]].max,data[data[now].ch[1]].max);
}

(bzoj卡到39S......)

转载于:https://www.cnblogs.com/nietzsche-oier/p/8747556.html

  • 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、付费专栏及课程。

余额充值