题面传送门

我!竟!然!独!立!A!C!了!这!道!题!incredible!

首先看到这类最大化某个分式的题目,可以套路地想到分数规划,考虑二分答案 \(mid\) 并检验是否存在合法的 \(S\) 使得 \(\dfrac{\sum\limits_{e\in S}v(e)}{|S|}\ge mid\),将分母乘过去并稍微变个形可得 \(\sum\limits_{e\in S}v(e)-mid\ge 0\),也就是说我们将每条边边权都减去 \(mid\) 并检验包含 \([L,R]\) 条边的路径权值和的最大值是否 \(\ge 0\),于是现在题目转化为怎样求包含 \([L,R]\) 条边路径权值和的最大值。

我们记 \(f_{x,i}\) 为对于 \(x\) 的子树中所有深度为 \(i\) 的点 \(y\),\(x\) 到 \(y\) 距离的最大值,那么显然有状态转移方程 \(dp_{x,i}=\max\limits_{y\in son(x)}dp_{y,i}+v(x,y)\),其中 \(v(x,y)\) 为 \(x,y\) 之间边的边权,显然我们可以像点分治那样在遍历子树,更新 \(f_{x,i}\) 的过程中更新答案。但暴力遍历更新是 \(\mathcal O(n^2)\) 的,无法通过。考虑优化,这时候长链剖分就要派上用场了,我们建一棵全局线段树,实时维护当前 \(f_{x,i}\) 的值,我们遍历深儿子 \(y\) 的时候就直接将其 \(f_{y,i}\) 的值继承到 \(f_{x,i}\) 上,当然由于贡献中有一个 \(v(x,y)\),因此 \(f_{x,i}\) 的值应当为 \(f_{y,i}\) 加上 \(v(x,y)\),这个可以通过线段树区间加解决,即在线段树上 \([dep_x+1,mxdep_x]\) 的值全部加上 \(v(x,y)\)。遍历浅儿子 \(u\)的时候,我们就在第一次遍历时开个临时变量(vector<vector<double> >)记录下所有 \(f_{u,j}\) 的值——显然对于每个浅儿子 \(u\),\(f_{u,j}\) 只在 \(j\in[dep_u,mxdep_u]\) 时值不是 \(-\infty\),因此我们只需记录下 \(mxdep_u-dep_u+1\) 个这样的值即可,根据所有链长之和为线性可知对于所有 \(x\),我们总共最多保存 \(\mathcal O(n)\) 个这样的值。然后第二遍遍历时直接调用之前保存下来的值,在线段树上单点修改即可,贡献就随便乱搞搞,算一下即可,相当于在线段树上执行一遍区间查询。

总复杂度 \(n\log^2n\),其中一个 \(\log\) 来源于线段树,一个 \(\log\) 来源于分数规划中的二分。

代码可能要开个 O2 才能过:

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1e5;
const double INF=1e12;
const double EPS=1e-4;
int n,L,R,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],val[MAXN*2+5],ec=0;
void adde(int u,int v,int w){to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
struct node{int l,r;double mx,lz;} s[MAXN*4+5];
void pushup(int k){s[k].mx=max(s[k<<1].mx,s[k<<1|1].mx);}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;s[k].mx=-INF;s[k].lz=0;if(l==r) return;
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void pushdown(int k){
if(fabs(s[k].lz)>EPS){
s[k<<1].lz+=s[k].lz;s[k<<1].mx+=s[k].lz;
s[k<<1|1].lz+=s[k].lz;s[k<<1|1].mx+=s[k].lz;
s[k].lz=0;
}
}
void add(int k,int l,int r,double v){
if(l<=s[k].l&&s[k].r<=r){s[k].mx+=v;s[k].lz+=v;return;}
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) add(k<<1,l,r,v);
else if(l>mid) add(k<<1|1,l,r,v);
else add(k<<1,l,mid,v),add(k<<1|1,mid+1,r,v);
pushup(k);
}
void modify(int k,int p,double x){
if(s[k].l==s[k].r){chkmax(s[k].mx,x);return;}
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(p<=mid) modify(k<<1,p,x);
else modify(k<<1|1,p,x);
pushup(k);
}
void clear(int k,int p){
if(s[k].l==s[k].r){s[k].mx=-INF;return;}
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(p<=mid) clear(k<<1,p);
else clear(k<<1|1,p);
pushup(k);
}
double query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r) return s[k].mx;
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return max(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
int dep[MAXN+5],mxdep[MAXN+5],dson[MAXN+5],ed[MAXN+5];
void dfs0(int x=1,int f=0){
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(y==f) continue;
dep[y]=mxdep[y]=dep[x]+1;dfs0(y,x);
if(mxdep[y]>mxdep[x]) mxdep[x]=mxdep[y],dson[x]=y,ed[x]=z;
}
}
double ans=-INF;
void dfs(int x,int f,double v){
vector<pair<int,vector<double> > > t;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(y==f||y==dson[x]) continue;
dfs(y,x,v);vector<double> tmp;
for(int j=dep[y];j<=mxdep[y];j++) tmp.pb(query(1,j,j)),clear(1,j);
t.pb(mp(z,tmp));
} if(dson[x]){
dfs(dson[x],x,v);
add(1,dep[x]+1,mxdep[x],ed[x]-v);
} chkmax(ans,query(1,min(dep[x]+L,n),min(dep[x]+R,n)));
modify(1,dep[x],0);
for(int i=0;i<t.size();i++){
for(int j=0;j<t[i].se.size();j++){
double vv=t[i].fi-v+t[i].se[j];
if(R-j-1<0) continue;
int lwb=max(L-j-1,0),upb=R-j-1;
chkmax(ans,query(1,min(dep[x]+lwb,n),min(dep[x]+upb,n))+vv);
}
for(int j=0;j<t[i].se.size();j++)
modify(1,dep[x]+1+j,t[i].fi-v+t[i].se[j]);
}
}
bool check(double mid){
build(1,0,n);ans=-INF;dfs(1,0,mid);
// printf("%.3lf\n",ans);
return ans>-EPS;
}
int main(){
scanf("%d%d%d",&n,&L,&R);
for(int i=1,u,v,w;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
adde(u,v,w);adde(v,u,w);
} dfs0();//check(2.5);
double l=0,r=1e6,x=-114514.1919810;
while(fabs(r-l)>EPS){
double mid=(l+r)/2.0;
if(check(mid)) x=mid,l=mid;
else r=mid;
} printf("%.3lf\n",x);
return 0;
}

洛谷 P4292 - [WC2010]重建计划(长链剖分+线段树)的更多相关文章

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

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

  2. [WC2010]重建计划 长链剖分

    [WC2010]重建计划 LG传送门 又一道长链剖分好题. 这题写点分治的人应该比较多吧,但是我太菜了,只会长链剖分. 如果你还不会长链剖分的基本操作,可以看看我的长链剖分总结. 首先一看求平均值最大 ...

  3. 「WC2010」重建计划(长链剖分/点分治)

    「WC2010」重建计划(长链剖分/点分治) 题目描述 有一棵大小为 \(n\) 的树,给定 \(L, R\) ,要求找到一条长度在 \([L, R]\) 的路径,并且路径上边权的平均值最大 \(1 ...

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

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

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

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

  6. 2019.01.21 bzoj1758: [Wc2010]重建计划(01分数规划+长链剖分+线段树)

    传送门 长链剖分好题. 题意简述:给一棵树,问边数在[L,R][L,R][L,R]之间的路径权值和与边数之比的最大值. 思路: 用脚指头想都知道要01分数规划. 考虑怎么checkcheckcheck ...

  7. [WC2010]重建计划(长链剖分+线段树+分数规划)

    看到平均值一眼分数规划,二分答案mid,边权变为w[i]-mid,看是否有长度在[L,R]的正权路径.设f[i][j]表示以i为根向下j步最长路径,用长链剖分可以优化到O(1),查询答案线段树即可,复 ...

  8. BZOJ.3653.谈笑风生(长链剖分/线段树合并/树状数组)

    BZOJ 洛谷 \(Description\) 给定一棵树,每次询问给定\(p,k\),求满足\(p,a\)都是\(b\)的祖先,且\(p,a\)距离不超过\(k\)的三元组\(p,a,b\)个数. ...

  9. 2018牛客网暑假ACM多校训练赛(第七场)I Tree Subset Diameter 动态规划 长链剖分 线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round7-I.html 题目传送门 -  https://www.n ...

随机推荐

  1. 【UE4 C++ 基础知识】<15> 智能指针 TSharedPtr、UniquePtr、TWeakPtr、TSharedRef

    基本概念 UE4 对 UObject 对象提供垃圾回收 UE4 对原生对象不提供垃圾回收,需要手动进行清理 方式 malloc / free new / delete new与malloc的区别在于, ...

  2. [对对子队]会议记录5.20(Scrum Meeting7)

    今天已完成的工作 马嘉 ​ 工作内容:录制新手引导视频 ​ 相关issue:优化顺序关卡新手引导功能 ​ 相关签入:feat: 录制了新的新手引导视频 吴昭邦 ​ 工作内容:增加加速功能 ​ 相关is ...

  3. Noip模拟77 2021.10.15

    T1 最大或 $T1$因为没有开$1ll$右移给炸掉了,调了一年不知道为啥,最后实在不懂了 换成$pow$就过掉了,但是考场上这题耽误了太多时间,后面的题也就没办法好好打了.... 以后一定要注意右移 ...

  4. AGC019F

    题目大意 $n$ + $m$ 个问题,其中$n$ 个答案是$YES$,$m$个是$NO$的,你依次答题,每答一道,就可以立刻知道这道题的答案,求在最优策略下答错次数的期望,对$998244353$取模 ...

  5. Swift进阶-内存管理

    本文的主要目的是探索 RefCount 的内存结构及强/弱引用计数管理 Swift 中也是采用 ARC 编译器自动内存管理机制. Swift 对象的内存结构是 HeapObject, 有两个属性 Me ...

  6. 流媒体技术的应用,如何搭建一个SimpleNVR流媒体服务系统

    Onvif/RTSP流媒体服务 SimpleNVR Onvif/RTSP流媒体服务是一款软硬一体音视频流媒体服务软件.它是在5G.AI.云计算.大数据.物联网等网络技术大规模商用后,用户要求视频随时随 ...

  7. ELK集群之filebeat(6)

    filebeat工作原理 ilebeat是本地文件的日志数据采集器. 作为服务器上的代理安装,Filebeat监视日志目录或特定日志文件,tail file,并将它们转发给Elasticsearch或 ...

  8. css语法规范、选择器、字体、文本

    css语法规范 使用 HTML 时需要遵从一定的规范,CSS 也是如此.要想熟练地使用 CSS 对网页进行修饰,首先需要了解CSS 样式规则. CSS 规则由两个主要的部分构成:选择器以及一条或多条声 ...

  9. NOIP模拟92&93(多校26&27)

    前言 由于太菜了,多校26 只改出来了 T1 ,于是直接并在一起写啦~~~. T0 NOIP 2018 解题思路 第一次考场上面写三分,然而我并不知道三分无法处理不是严格单峰的情况,但凡有一个平台都不 ...

  10. Linux ns 3. Mnt Namespace 详解

    1. 文件系统层次化 对 Linux 系统来说一切皆文件,Linux 使用树形的层次化结构来管理所有的文件对象. 完整的Linux文件系统,是由多种设备.多种文件系统组成的一个混合的树形结构.我们首先 ...