传送门


看到平均数最大,自然地想到二分答案。那么我们的$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. 【转】解决configure: error: C++ compiler cannot create executables问题

    转自:http://www.coderbolg.com/content/83.html 啊……天啊,./configure时报错:configure: error: C++ compiler cann ...

  2. Fiddler 使用fiddler发送捕获的请求及模拟服务器返回

    使用fiddler发送捕获的请求及模拟服务器返回 by:授客 QQ:1033553122 1.做好相关监听及代理设置 略 2.发送捕获的请求 如图 3.模拟服务器返回 本例的一个目的是,根据服务器返回 ...

  3. Android basics

    只要是Android中的控件,最终都继承自View.

  4. [LTR] RankLib.jar 包介绍

    一.介绍 RankLib.jar 是一个学习排名(Learning to rank)算法的库,目前已经实现了如下几种算法: MART RankNet RankBoost AdaRank Coordin ...

  5. Android 电池系列

    android 电池(一):锂电池基本原理篇 android 电池(二):android关机充电流程.充电画面显示 android 电池(三):android电池系统 android电池(四):电池 ...

  6. CentOS7.4下编译Hadoop-2.7.6

    1. 使用系统和软件 操作系统:CentOS Linux release 7.4.1708 (Core) 64位 使用软件如下: jdk1.8.0_112.tar.gz hadoop-2.7.6-sr ...

  7. 2018. first week now at home

    外面雪刚停. 现在是2018.1.5 2018 needs to consider next steps了.未雨绸缪啊     下面是2017年last working day   外面黑了,水面上黑 ...

  8. css固定背景图位置 实现屏幕滚动时 显示背景图不同区域

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  9. jQuery 往table添加新内容有以下四个方法:

    Query 添加新内容有以下四个方法: append() - 在被选元素的结尾插入内容 prepend() - 在被选元素的开头插入内容 after() - 在被选元素之后插入内容 before() ...

  10. 【数据库】事务,ACID,CAP和一致性

    什么是事务 事务是指由一系列数据库操作组成的一个完整的逻辑过程,这个过程中的所有操作要么都成功,要么都不成功.比如:常见的例子就是银行转账的例子,一次转账操作会包含多个数据库操作,而这些数据库操作需要 ...