BZOJ1758 WC2010 重建计划 二分答案、点分治、单调队列
看到平均数最大,自然地想到二分答案。那么我们的$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 重建计划 二分答案、点分治、单调队列的更多相关文章
- bzoj 1758 [Wc2010]重建计划 分数规划+树分治单调队列check
[Wc2010]重建计划 Time Limit: 40 Sec Memory Limit: 162 MBSubmit: 4345 Solved: 1054[Submit][Status][Disc ...
- BZOJ.1758.[WC2010]重建计划(分数规划 点分治 单调队列/长链剖分 线段树)
题目链接 BZOJ 洛谷 点分治 单调队列: 二分答案,然后判断是否存在一条长度在\([L,R]\)的路径满足权值和非负.可以点分治. 对于(距当前根节点)深度为\(d\)的一条路径,可以用其它子树深 ...
- [WC2010]重建计划(分数规划+点分治+单调队列)
题目大意:给定一棵树,求一条长度在L到R的一条路径,使得边权的平均值最大. 题解 树上路径最优化问题,不难想到点分治. 如果没有长度限制,我们可以套上01分数规划的模型,让所有边权减去mid,求一条路 ...
- 【BZOJ 1758】【WC 2010】重建计划 分数规划+点分治+单调队列
一开始看到$\frac{\sum_{}}{\sum_{}}$就想到了01分数规划但最终还是看了题解 二分完后的点分治,只需要维护一个由之前处理过的子树得出的$tb数组$,然后根据遍历每个当前的子树上的 ...
- BZOJ1758: [Wc2010]重建计划
题解: 这题我居然做了一星期?... 平均值的极值其实也可以算是一种分数规划,只不过分母上b[i]=1 然后我们就可以二分这个值.类似与 HNOI最小圈 如果没有 链的长度的限制的话,我们直接两遍df ...
- BZOJ1758[Wc2010]重建计划——分数规划+长链剖分+线段树+二分答案+树形DP
题目描述 输入 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai, ...
- [BZOJ1758][WC2010]重建计划(点分治+单调队列)
点分治,对于每个分治中心,考虑求出经过它的符合长度条件的链的最大权值和. 从分治中心dfs下去取出所有链,为了防止两条链属于同一个子树,我们一个子树一个子树地处理. 用s1[i]记录目前分治中心伸下去 ...
- BZOJ1758: [Wc2010]重建计划(01分数规划+点分治+单调队列)
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1758 01分数规划,所以我们对每个重心进行二分.于是问题转化为Σw[e]-mid>=0, ...
- bzoj1758 [Wc2010]重建计划 & bzoj2599 [IOI2011]Race
两题都是树分治. 1758这题可以二分答案avgvalue,因为avgvalue=Σv(e)/s,因此二分后只需要判断Σv(e)-s*avgvalue是否大于等于0,若大于等于0则调整二分下界,否则调 ...
随机推荐
- AppBoxPro(权限管理框架--FineUIPro基础版+工厂模式+ADO.NET+存储过程)
FineUIPro基础版火爆来袭,特献上ADO.NET纯SQL方式AppBoxPro,希望大家能够喜欢! 下载源码请到[知识星球] https://t.zsxq.com/3rrNFyv
- Git应用—03分支管理和冲突解决(转载)
Git 分支管理和冲突解决 https://www.cnblogs.com/mengdd/p/3585038.html 创建分支 git branch 没有参数,显示本地版本库中所有的本地分支名称. ...
- 如何创建和还原SQL Server 2005数据库?
在还原SQL Server 2005数据库文件之前,建议先把要还原的数据库文件复制粘贴到某个盘的根目录下,这样便于一会儿找到相关的文件,比如C盘. 先打开SQL Server 2005的Microso ...
- (转载)Oracle 树操作(select…start with…connect by…prior)
转载地址:https://www.cnblogs.com/linjiqin/p/3152674.html 备注:如有侵权,请立即联系删除. oracle树查询的最重要的就是select…start w ...
- mysqlreport工具
进行MySQL的配置优化,首先必须找出MySQL的性能瓶颈所在:而SHOW STATUS输出的报告正是用来计算性能瓶颈的参考数据.mysqlreport不像SHOW STATUS那样简单的罗列数据,而 ...
- PCA与KPCA
PCA是利用特征的协方差矩阵判断变量间的方差一致性,寻找出变量之间的最佳的线性组合,来代替特征,从而达到降维的目的,但从其定义和计算方式中就可以看出,这是一种线性降维的方法,如果特征之间的关系是非线性 ...
- Flask消息闪现
目录 Flask消息闪现 简单的例子 闪现消息的类别 过滤闪现消息 Message Flashing 参考 Flask消息闪现 一个好的应用和用户界面都需要良好的反馈.如果用户得不到足够的反馈,那么应 ...
- 32_使用BeanUtils工具包操作JavaBean
由于对属性设置值和得到值的需求很多,使用频率很高,所以有一些开源勇士 不满足于JavaBean API 中IntroSpector来操作bean, 写出来了通用的BeanUtils工具,来进一步简 ...
- 【PAT】1083 是否存在相等的差(20 分)
//这题不是我耍流氓,实在太简单,只能直接贴代码了,凑个数 #include<stdio.h> int aaa[10005]={0}; int main(){ int N;scanf(&q ...
- percona-toolkit大表操作DDL使用
1. 系统与安装数据库 [root@zhang ~]# cat /etc/redhat-release # 也可以使用其他版本 CentOS Linux release (Core) [root@zh ...