传送门


看到平均数最大,自然地想到二分答案。那么我们的$check$函数就是要求:是否存在一条长度在$[L,U]$的路径,满足其权值和$\geq 0$。

看到长度在$[L,U]$,自然地想到点分治求解。我们考虑如何统计答案,像树的难题那样使用线段树的话,复杂度会变成$nlog^3n$,显然是跑不过这道题的。

我们考虑:将一棵子树内的路径按照长度排序,那么从前往后依次询问子树内每一条路径的贡献的时候,在已经搜过的子树中可以匹配的长度区间是一个单调的区间。那么我们就可以使用单调队列将合并过程优化为$O(n)$,均摊到每一条路径上就是$O(1)$的转移,那么总复杂度就变为了$O(nlogn)$。每一次查询完一棵子树再用这棵子树的路径长度对应的答案更新已经搜过的子树中记录的答案,继续解决之后的问题。

但是先别急着写,考虑这样一种情况:$L=1,U=\frac{N}{2}$,数据先构造了一条点数为$\frac{N}{2}$的链,然后在链的一端构造了一个点数为$\frac{N}{2}$的菊花。你的分治中心显然是会选在菊花的花蕊处,然后你第一棵子树选择访问了那一条链,更新了长度在$1$到$\frac{N}{2}$的最优答案。然后你去访问菊花中的每一个点的时候,每一次都需要跑$\frac{N}{2}$次对单调队列进行初始化,然后你这一层分治的复杂度就变成了$O(N^2)$。

那么这个问题的根源是什么呢?在于每一次单调队列初始化的复杂度实际上是$O(min(U - 1 , maxLen))$,其中$maxLen$为之前访问的路径的最长长度。如果我们在本身$U$就比较大的情况下,先选择将$maxLen$变到很大,然后再不断地进行单调队列的操作,那复杂度就自然上天了。

那么解决方法是什么呢?一个有效的方法是按照子树的$maxLen$从小到大访问子树,这样每一次分治中单调队列初始化的复杂度总和就能控制在$O(\text{分治区域大小})$级别,就有正确的$O(nlog^2n)$的复杂度了。

因为这道题卡常较为严重,推荐能够初始化的内容尽量初始化,如果每一次分治的过程中重新求很多东西你就会获得$20pts$的好成绩qwq

#include<bits/stdc++.h>
#define ld long double
#define eps 1e-8
#define int long long
//This code is written by Itst
using namespace std; inline int read(){
int a = ;
bool f = ;
char c = getchar();
while(c != EOF && !isdigit(c)){
if(c == '-')
f = ;
c = getchar();
}
while(c != EOF && isdigit(c)){
a = (a << ) + (a << ) + (c ^ '');
c = getchar();
}
return f ? -a : a;
} const int MAXN = ;
struct Edge{
int end , upEd , w;
}Ed[MAXN << ];
int head[MAXN] , size[MAXN];
int N , L , U , cntEd , nowSize , minSize , minInd;
vector < vector < int > > be[MAXN];
vector < int > temp , pot;
deque < int > q;
bool vis[MAXN]; bool cmp(vector < int > a , vector < int > b){
return a.size() < b.size();
} inline void addEd(int a , int b , int c){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
Ed[cntEd].w = c;
head[a] = cntEd;
} void getSize(int x){
vis[x] = ;
++nowSize;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end])
getSize(Ed[i].end);
vis[x] = ;
} void getRoot(int x){
vis[x] = size[x] = ;
int maxN = ;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end]){
getRoot(Ed[i].end);
size[x] += size[Ed[i].end];
maxN = max(maxN , size[Ed[i].end]);
}
maxN = max(maxN , nowSize - size[x]);
if(maxN < minSize){
minSize = maxN;
minInd = x;
}
vis[x] = ;
} void dfs(int x , int dep , int w){
if(dep > U)
return;
if(temp.size() == dep)
temp.push_back(w);
else
temp[dep] = max(temp[dep] , w);
vis[x] = ;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end])
dfs(Ed[i].end , dep + , w + Ed[i].w);
vis[x] = ;
} void init(int x){
nowSize = ;
minSize = 0x7fffffff;
getSize(x);
getRoot(x);
int root = minInd;
vis[root] = ;
for(int i = head[root] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end]){
temp.push_back();
dfs(Ed[i].end , , Ed[i].w);
be[root].push_back(temp);
temp.clear();
}
sort(be[root].begin() , be[root].end() , cmp);
for(int i = head[root] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end])
init(Ed[i].end);
} inline vector < int > merge(vector < int > a , vector < int > b){
if(a.size() < b.size())
swap(a , b);
for(int i = ; i < b.size() ; ++i)
a[i] = max(a[i] , b[i]);
return a;
} inline bool judge(vector < int > a , vector < int > b , ld mid){
if(!b.size())
return ;
q.clear();
int p = ;
for(int i = b.size() - ; i >= ; --i){
while(p < a.size() && p + i <= U){
while(!q.empty() && a[q.back()] - mid * q.back() < a[p] - mid * p)
q.pop_back();
q.push_back(p++);
}
while(!q.empty() && q.front() + i < L)
q.pop_front();
if(!q.empty() && a[q.front()] - mid * q.front() + b[i] - mid * i > -eps)
return ;
}
return ;
} bool check(ld mid){
for(int i = ; i <= N ; ++i){
if(!be[i].size())
continue;
pot.clear();
for(int j = ; j < be[i].size() ; ++j){
if(judge(pot , be[i][j] , mid))
return ;
pot = merge(pot , be[i][j]);
}
}
return ;
} signed main(){
#ifndef ONLINE_JUDGE
freopen("4292.in" , "r" , stdin);
//freopen("4292.out" , "w" , stdout);
#endif
N = read();
L = read();
U = read();
ld L = 1e6 , R = ;
for(int i = ; i < N ; ++i){
int a = read() , b = read() , c = read();
addEd(a , b , c);
addEd(b , a , c);
L = min(L , (ld)c);
R = max(R , (ld)c);
}
init();
while(R - L > eps){
ld mid = (L + R) / ;
check(mid) ? L = mid : R = mid;
}
printf("%.3Lf" , L);
return ;
}

BZOJ1758 WC2010 重建计划 二分答案、点分治、单调队列的更多相关文章

  1. bzoj 1758 [Wc2010]重建计划 分数规划+树分治单调队列check

    [Wc2010]重建计划 Time Limit: 40 Sec  Memory Limit: 162 MBSubmit: 4345  Solved: 1054[Submit][Status][Disc ...

  2. BZOJ.1758.[WC2010]重建计划(分数规划 点分治 单调队列/长链剖分 线段树)

    题目链接 BZOJ 洛谷 点分治 单调队列: 二分答案,然后判断是否存在一条长度在\([L,R]\)的路径满足权值和非负.可以点分治. 对于(距当前根节点)深度为\(d\)的一条路径,可以用其它子树深 ...

  3. [WC2010]重建计划(分数规划+点分治+单调队列)

    题目大意:给定一棵树,求一条长度在L到R的一条路径,使得边权的平均值最大. 题解 树上路径最优化问题,不难想到点分治. 如果没有长度限制,我们可以套上01分数规划的模型,让所有边权减去mid,求一条路 ...

  4. 【BZOJ 1758】【WC 2010】重建计划 分数规划+点分治+单调队列

    一开始看到$\frac{\sum_{}}{\sum_{}}$就想到了01分数规划但最终还是看了题解 二分完后的点分治,只需要维护一个由之前处理过的子树得出的$tb数组$,然后根据遍历每个当前的子树上的 ...

  5. BZOJ1758: [Wc2010]重建计划

    题解: 这题我居然做了一星期?... 平均值的极值其实也可以算是一种分数规划,只不过分母上b[i]=1 然后我们就可以二分这个值.类似与 HNOI最小圈 如果没有 链的长度的限制的话,我们直接两遍df ...

  6. BZOJ1758[Wc2010]重建计划——分数规划+长链剖分+线段树+二分答案+树形DP

    题目描述 输入 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai, ...

  7. [BZOJ1758][WC2010]重建计划(点分治+单调队列)

    点分治,对于每个分治中心,考虑求出经过它的符合长度条件的链的最大权值和. 从分治中心dfs下去取出所有链,为了防止两条链属于同一个子树,我们一个子树一个子树地处理. 用s1[i]记录目前分治中心伸下去 ...

  8. BZOJ1758: [Wc2010]重建计划(01分数规划+点分治+单调队列)

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1758 01分数规划,所以我们对每个重心进行二分.于是问题转化为Σw[e]-mid>=0, ...

  9. bzoj1758 [Wc2010]重建计划 & bzoj2599 [IOI2011]Race

    两题都是树分治. 1758这题可以二分答案avgvalue,因为avgvalue=Σv(e)/s,因此二分后只需要判断Σv(e)-s*avgvalue是否大于等于0,若大于等于0则调整二分下界,否则调 ...

随机推荐

  1. 【代码笔记】Web-利用Dreamweaver实现form

    一,打开Dreamweaver---->File---New---->如下图所示.选择HTML,点击OK. 二,会出现如下图所示界面.把光标放到Body处. 三,将上面的栏切换到Desig ...

  2. Salesforce自定义权限简介

    自定义权限(Custom Permission) Salesforce默认提供了多种方式设定用户的权限,比如简档.权限集等.在这些设定中,已经包括了系统中的对象.应用.字段.页面布局等组件,管理员或开 ...

  3. CSS| 框模型-定位及相關屬性

    CSS 定位 (Positioning) 属性允许你对元素进行定位. CSS 定位和浮动 CSS 为定位和浮动提供了一些属性,利用这些属性,可以建立列式布局,将布局的一部分与另一部分重叠,还可以完成多 ...

  4. [SequenceFile_3] MapFile

    0. 说明 MapFile 介绍 && 测试 1. 介绍 对 MapFile 的介绍如下: MapFile 是带有索引的 SequenceFile MapFile 是排序的 Seque ...

  5. python+mongodb+flask的基本使用

    最近在做一个设备管理系统的后端,需要用python结合mongodb来实现,查了一下flask框架是比较合适的,自己摸索了好久一步步慢慢实现基本功能. 在程序开始之前请确保mongodb服务是开启的, ...

  6. 5.3Python函数(三)

    目录 目录 前言 (一)装饰器 ==1.简单的装饰器== ==2.修饰带参数函数的装饰器== ==3.修饰带返回值函数的装饰器== ==4.自身带参数的装饰器== (二)迭代器 (三)生成器 ==1. ...

  7. 薛兆丰吴军何帆曾鸣万维刚李笑来罗永浩等得到APP专栏作者的书23本

    最近看了何帆的<大局观>,是他在得到APP的专栏文章的精选.顺便整理以下最近两三年内看过的得到APP其他专栏与课程作者的得到精选文集和他们写过的其他的书共23本. 薛兆丰 4星|<薛 ...

  8. Java设计模式之一 ----- 单例模式

    什么是单例模式 保证一个系统中的某个类只有一个实例而且该实例易于外界访问.例如Windows界面的任务管理器就可以看做是一个单例. 单例模式的使用场景 需要频繁的进行创建和销毁的对象: 创建对象时耗时 ...

  9. 【原创】Linux常用命令记录

    1. 查看网络状态分布 #!/bin/sh netstat -apn >/dev/ \ | awk 'BEGIN {printf("%-15s%-15s%-15s%-15s\n&quo ...

  10. Sketch网页截屏插件设计开发

    1.需求 在Sketch的Artboard中插入网页截图: 1.1.输入网址,自动截图到Artboard中,并居中显示: 1.2.可截取网页局部图片 2.技术选型 技术的选型主要是针对截图功能的选型, ...