题目描述

题目传送门

分析

看到比值的形式就想到 \(01分数规划\),二分答案

设当前的值为 \(mids\)

如果存在\(\frac{\sum _{e \in S} v(e)}{|S|} \geq mids\)

那么 \(\sum _{e \in S} v(e)-|S| \times mids \geq 0\)

我们把每一条边的权值减去 \(mids\)

问题就变成了找出一条长度在 \([l,r]\) 之间的简单路径

是的路径的长度大于等于 \(0\)

我们可以开一个权值线段树去维护,但这样复杂度就变成了 \(nlog^3n\)

实际上,我们可以把寻找路径时的 \(dfs\) 换成 \(bfs\)

这样就相当于自动给路径的长度排好了序,可以直接用单调队列维护

因为每一次单调队列都要从 \(0\) 枚举到 \(min(r,maxdep)\)

因为你记录最大值的数组最多只会更新到 \(maxdep\),所以枚举多了没用

为了保证复杂度,我们要提前把子树按照子树内最大深度从小到大排序,使得 \(maxdep\) 尽可能小

还可以提前建出点分树,避免每一次 \(dfs\) 重新找重心浪费时间

时间复杂度 \(nlog^2n\)

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=4e5+5;
int h[maxn],tot=1,n,m,lef,rig;
struct asd{
int to,nxt,preval;
double val;
}b[maxn];
void ad(rg int aa,rg int bb,rg int cc){
b[tot].to=bb;
b[tot].nxt=h[aa];
b[tot].preval=cc;
h[aa]=tot++;
}
struct Node{
int num,dep,id;
Node(){}
Node(rg int aa,rg int bb,rg int cc){
num=aa,dep=bb,id=cc;
}
};
std::vector<Node> pre[maxn];
bool cmp(rg Node aa,rg Node bb){
return aa.dep<bb.dep;
}
int dep[maxn];
void dfs(rg int now,rg int lat,rg int nowdep){
dep[now]=nowdep;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat) continue;
dfs(u,now,nowdep+1);
dep[now]=std::max(dep[now],dep[u]);
}
}//预处理子树最大深度
int siz[maxn],maxsiz[maxn],rt,totsiz,jud[maxn],tim,tp;
bool vis[maxn];
std::vector<int> g[maxn];
void getroot(rg int now,rg int lat){
siz[now]=1,maxsiz[now]=0;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat || vis[u]) continue;
getroot(u,now);
siz[now]+=siz[u];
maxsiz[now]=std::max(maxsiz[now],siz[u]);
}
maxsiz[now]=std::max(maxsiz[now],totsiz-siz[now]);
if(maxsiz[now]<maxsiz[rt]) rt=now;
}//找重心
void predfs(rg int now){
vis[now]=1;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(!vis[u]){
pre[now].push_back(Node(u,dep[u],i));
}
}
std::sort(pre[now].begin(),pre[now].end(),cmp);
for(rg int i=0;i<pre[now].size();i++){
rg int u=pre[now][i].num;
totsiz=siz[u],rt=0;
getroot(u,now);
g[now].push_back(rt);
predfs(rt);
}
}//建好点分树,并按照最大深度从小到大排序
double ans=-1e7,mmax[maxn];
struct jie{
double val;
int dep;
jie(){}
jie(rg double aa,rg int bb){
val=aa,dep=bb;
}
}sta[maxn];
int q1[maxn],q2[maxn],nhead,ntail;
double q3[maxn];
void bfs(rg int now,rg double nval,rg int ndep){
tim++;
jud[now]=tim,nhead=ntail=1;
q1[1]=now,q2[1]=ndep,q3[1]=nval;
rg int now1,now2;
rg double now3;
while(nhead<=ntail){
now1=q1[nhead],now2=q2[nhead],now3=q3[nhead];
sta[++tp]=jie(now3,now2);
nhead++;
for(rg int i=h[now1];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(jud[u]==tim || vis[u]) continue;
jud[u]=tim;
ntail++;
q1[ntail]=u,q2[ntail]=now2+1,q3[ntail]=now3+b[i].val;
}
}
}//bfs寻找路径
int q[maxn],head,tail;
void solve(rg int now){
vis[now]=1,tp=1;
rg int beg=0,cs=0,tl=0,tr=0,maxdep=0;
mmax[0]=0;
for(rg int i=0;i<pre[now].size();i++){
rg int u=pre[now][i].num;
beg=tp+1;
bfs(u,b[pre[now][i].id].val,1);
head=1,tail=0,cs=beg;
for(rg int j=std::min(rig,maxdep);j>=0;j--){
tl=j>=lef?0:lef-j,tr=rig-j;
while(head<=tail && sta[q[head]].dep<tl) head++;
while(cs<=tp && sta[cs].dep<tl) cs++;
while(cs<=tp && sta[cs].dep<=tr){
while(head<=tail && sta[cs].val>=sta[q[tail]].val) tail--;
q[++tail]=cs++;
}
if(head<=tail) ans=std::max(ans,sta[q[head]].val+mmax[j]);
}
for(rg int j=beg;j<=tp;j++) mmax[sta[j].dep]=std::max(mmax[sta[j].dep],sta[j].val);
maxdep=std::max(maxdep,sta[tp].dep);
}
if(ans>=0) return;
for(rg int i=1;i<=tp;i++) mmax[sta[i].dep]=-1e9;
for(rg int i=0;i<g[now].size();i++) solve(g[now][i]);
}//点分治核心+单调队列
bool pd(rg double mids){
memset(vis,0,sizeof(vis));
memset(jud,0,sizeof(jud));
for(rg int i=0;i<maxn;i++) mmax[i]=-1e7;
ans=-1e7,tim=0;
for(rg int i=1;i<tot;i++) b[i].val=(double)b[i].preval-mids;
solve(rt);
return ans>=0;
}//判断答案是否合法
int main(){
memset(h,-1,sizeof(h));
n=read(),lef=read(),rig=read();
rg int aa,bb,cc;
for(rg int i=1;i<n;i++){
aa=read(),bb=read(),cc=read();
ad(aa,bb,cc);
ad(bb,aa,cc);
}
dfs(1,0,0);
maxsiz[0]=0x3f3f3f3f,rt=0,totsiz=n;
getroot(1,0);
predfs(rt);
totsiz=n,rt=0;
memset(vis,0,sizeof(vis));
getroot(1,0);
double l=0,r=1e6,mids;
for(rg int i=1;i<=33;i++){
mids=(l+r)/2.0;
if(pd(mids)) l=mids;
else r=mids;
}
printf("%.3f\n",mids);
return 0;
}

P4292 [WC2010]重建计划 点分治+单调队列的更多相关文章

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

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

  2. 洛谷 P4292 [WC2010]重建计划 解题报告

    P4292 [WC2010]重建计划 题目描述 \(X\)国遭受了地震的重创, 导致全国的交通近乎瘫痪,重建家园的计划迫在眉睫.\(X\)国由\(N\)个城市组成, 重建小组提出,仅需建立\(N-1\ ...

  3. P4292 [WC2010]重建计划

    无脑上二分+淀粉质完事了 每个子树算的时候把儿子按照最长路径从小到大依次做,和前面的单调队列算一波,每个儿子的复杂度不超过这个子树大小 // luogu-judger-enable-o2 #inclu ...

  4. BZOJ 1758 / Luogu P4292 [WC2010]重建计划 (分数规划(二分/迭代) + 长链剖分/点分治)

    题意 自己看. 分析 求这个平均值的最大值就是分数规划,二分一下就变成了求一条长度在[L,R]内路径的权值和最大.有淀粉质的做法但是我没写,感觉常数会很大.这道题可以用长链剖分做. 先对树长链剖分. ...

  5. 并不对劲的bzoj1758:p4292:[WC2010]重建计划

    题目大意 \(n\)(\(n\leq10^5\))个点的一棵树,有边权\(w\),给定\(l,r\),求边数在\([l,r]\)中的路径的平均边权的最大值 题解 二分答案,判断时将边权变成\(w-mi ...

  6. 洛谷 P4292 - [WC2010]重建计划(长链剖分+线段树)

    题面传送门 我!竟!然!独!立!A!C!了!这!道!题!incredible! 首先看到这类最大化某个分式的题目,可以套路地想到分数规划,考虑二分答案 \(mid\) 并检验是否存在合法的 \(S\) ...

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

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

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

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

  9. BZOJ1758: [Wc2010]重建计划

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

随机推荐

  1. Jenkins环境搭建(8)-邮件未能正常发送

    昨天,在使用jenkins构建项目时,出现了个问题,问题是:jenkins控制台日志显示邮件发送成功,但实际没有成功. 此前,jenkins的配置,项目构建后,是能正常发送邮件的,可这次突然就不行了, ...

  2. 精尽Spring MVC源码分析 - 寻找遗失的 web.xml

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  3. docker redis 设置和使用

    1 开启docker 拉取redis镜像 1.1 桌面版docker   在镜像所在位置命令行执行 docker load -i redis.tar 1.2 开启redis docker run -p ...

  4. SpringBoot异步调用--@Async详解

    1. 概述   在日常开发中,为了提高主线程的效率,往往需要采用异步调用处理,例如系统日志等.在实际业务场景中,可以使用消息中间件如RabbitMQ.RocketMQ.Kafka等来解决.假如对高可用 ...

  5. 解决IDEA Maven下载依赖包速度慢问题

    右键项目,maven选项,"Open setting.xml"或"Create setting.xml",在 mirrors 节点添加下面代码. <mir ...

  6. [日常摸鱼]bzoj1038[ZJOI2008]瞭望塔-半平面交

    这回好好用半平面交写一次- 看了cls当年写的代码看了好久大概看懂了-cls太强辣 #include<cstdio> #include<iostream> #include&l ...

  7. Linux简单复习

    cd 命令:切换目录 ls命令:用于浏览目录下的文件或者文件夹 rm 命令:用于删除文件或者目录,用法 rm –rf test.txt (-r表示递归,-f表示强制) cp 命令:用于拷贝文件,用法, ...

  8. [GXYCTF2019]禁止套娃(无参RCE)

    [GXYCTF2019]禁止套娃 1.扫描目录 扫描之后发现git泄漏 使用githack读取泄漏文件 <?php include "flag.php"; echo &quo ...

  9. MySQL:判断逗号分隔的字符串中是否包含某个字符串 && 如何在一个以逗号分隔的列表中的一个字段中连接MySQL中的多对多关系中的数据

    需求:      sql语句中,判断以逗号分隔的字符串中是否包含某个特定字符串,类似于判断一个数组中是否包含某一个元素, 例如:判断 'a,b,c,d,e,f,g' 中是否包含 'a',sql语句如何 ...

  10. Python基础(上篇)

    本篇文章主要内容:变量.注释.运算符.关键字.数据类型. 在入手变量之前我们先来看看经典的编程语句 → hello world 在python3中输出到控制台的函数是print() print(&qu ...