Description
X国遭受了地震的重创, 导致全国的交通近乎瘫痪,重建家园的计划迫在眉睫。X国由N个城市组成, 重建小组提出,仅需建立N-1条道路即可使得任意两个城市互相可达。于是,重建小组很快提出了一个包含N-1条道路的方案,并满足城市之间两两可达,他们还计算评估了每条道路e建设之后可以带来的价值v(e)。
由于重建计划复杂而艰难,经费也有一定限制。因此,政府要求第一期重建工程修建的道路数目为k条,但需满足L ≤ k ≤ U, 即不应少于L条,但不超过U条。同时,为了最大化利用率,要求建设的这些道路恰好组成一条简单路径,即所建设的k条路径可以构成一个排列e1 = (p1, q1), e2 = (p2, q2), „, ek = (pk, qk), 对于 1 ≤ i < k, 有(qi = pi+1)。
重建小组打算修改他们的原有方案以满足要求,即在原有的N-1条道路中寻找一条路径S作为新的方案,使得新方案中的道路平均价值
最大。这里v(e)表示道路e的价值,|S|表示新方案中道路的条数。请你帮助重建小组寻找一个最优方案。 注: 在本题中L和U的设置将保证有解。
对于20%的数据,N ≤ 5 000;
另有30%的数据,N ≤ 100 000, 原有方案恰好为一条路径(链);
对于100%的数据,N ≤ 100 000, 1 ≤ L ≤ U ≤ N-1, vi ≤ 10^6。
Solution
敲个二分就变成了求一条长度在[L,R]内路径的权值和最大,然后我就不会做了
首先这是一棵树,显然点分治是可写的
然鹅我并不是很想写点分治因此这里有另一种方法
先对树长链剖分,我们像做dsu on tree一样先做长链,用线段树继承长链的全部信息,然后做其他儿子
查询的时候枚举一下路径的长度len,左半边直接找长度为len的最大权值,右半边线段树查询长度为[L-len,R-len]的区间即可
一个非常巧妙的地方在于同一个长度我们只需要记录最大权值,这也是长链剖分在这类问题中优于重链剖分的原因
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
using std:: max;
using std:: min;
typedef double db;
const db INF=1e15;
const int N=400005;
const int E=600005;
struct edge {int y,w,next; db v;} e[E];
int dep[N],mx[N],son[N],pos[N],num[N];
int ls[N],edCnt,tot,L,R,n;
db rec[N<<2],dis[N],tmp[N],ans;
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void add_edge(int x,int y,int w) {
e[++edCnt]=(edge) {y,w,ls[x],0}; ls[x]=edCnt;
e[++edCnt]=(edge) {x,w,ls[y],0}; ls[y]=edCnt;
}
void dfs1(int now,int fa) {
mx[now]=dep[now]=dep[fa]+1;
for (int i=ls[now];i;i=e[i].next) {
if (e[i].y==fa) continue;
dfs1(e[i].y,now);
mx[now]=max(mx[now],mx[e[i].y]);
if (mx[son[now]]<mx[e[i].y]) son[now]=e[i].y;
}
}
void dfs2(int now,int fa) {
pos[now]=++pos[0];
if (son[now]) dfs2(son[now],now);
for (int i=ls[now];i;i=e[i].next) {
if (e[i].y==fa||e[i].y==son[now]) continue;
dfs2(e[i].y,now);
}
}
void modify(int now,int tl,int tr,int x,db v) {
rec[now]=std:: max(rec[now],v);
if (tl==tr) return ;
int mid=(tl+tr)>>1;
if (x<=mid) modify(now<<1,tl,mid,x,v);
else modify(now<<1|1,mid+1,tr,x,v);
}
db query(int now,int tl,int tr,int l,int r) {
if (r<l) return -INF;
if (tl==l&&tr==r) return rec[now];
int mid=(tl+tr)>>1;
if (r<=mid) return query(now<<1,tl,mid,l,r);
if (l>mid) return query(now<<1|1,mid+1,tr,l,r);
db qx=query(now<<1,tl,mid,l,mid);
db qy=query(now<<1|1,mid+1,tr,mid+1,r);
return max(qx,qy);
}
void build_tree(int now,int tl,int tr) {
rec[now]=-INF;
if (tl==tr) {
num[tl]=now;
return ;
}
int mid=(tl+tr)>>1;
build_tree(now<<1,tl,mid);
build_tree(now<<1|1,mid+1,tr);
}
void solve(int now,int fa) {
modify(1,1,n,pos[now],dis[now]);
for (int i=ls[now];i;i=e[i].next) {
if (e[i].y==son[now]) {
dis[e[i].y]=dis[now]+e[i].v;
solve(e[i].y,now);
}
}
for (int i=ls[now];i;i=e[i].next) {
if (e[i].y==fa||e[i].y==son[now]) continue;
dis[e[i].y]=dis[now]+e[i].v;
solve(e[i].y,now);
rep(j,1,mx[e[i].y]-dep[now]) {
tmp[j]=rec[num[pos[e[i].y]+j-1]];
if (j<=R) {
db tttt=query(1,1,n,max(pos[now]+L-j,1),min(pos[now]+R-j,pos[now]+mx[now]-dep[now]));
ans=max(ans,tttt+tmp[j]-dis[now]*2);
}
}
rep(j,1,mx[e[i].y]-dep[now]) modify(1,1,n,pos[now]+j,tmp[j]);
}
ans=max(ans,query(1,1,n,pos[now]+L,min(pos[now]+R,pos[now]+mx[now]-dep[now]))-dis[now]);
}
int main(void) {
n=read(),L=read(),R=read();
rep(i,2,n) {
int x=read(),y=read(),w=read();
add_edge(x,y,w);
}
dfs1(1,0); dfs2(1,0);
db prt,l,r; rec[0]=-INF;
for (l=0,r=1000000;r-l>1e-5;) {
db mid=(l+r)*0.5;
rep(i,1,edCnt) e[i].v=-mid+e[i].w;
ans=-INF; build_tree(1,1,n);
solve(1,0);
if (ans<=0) r=mid;
else l=mid;
}
printf("%.3lf\n", (l+r)*0.5);
return 0;
}