[WC2010]重建计划
首先看到要让一个平均值最大,想到 \(0/1\) 分数规划
二分一个 \(mid\) 作为答案
如果存在
\(\displaystyle \frac{\sum_{e\in S}v(e)}{\sum_{i=1}^{|S|}1}>mid\)
那么
\(\displaystyle \sum_{e\in S}v(e)-mid>0\)
也就是所有的边权减掉 \(mid\) 之后找出一条长度在\([l,r]\)之间的大于\(0\)的路径
我们点分治
处理完一棵子树后就把这棵子树的信息后对于每个长度 \(i\) 得到长度为 \(i\) 的路径
的权值最大是多少。和之前维护的其他子树信息进行合并。
对于一条长度为 \(x\) 的路径,需要一条\([l-x,r-x]\) 的路径和这条路径拼在一起。
随着 \(x\) 减小,这个抉择区间向后移动,但是区间总长始终不变,是一个滑动窗口问题。
用一个单调队列维护一下就好了,复杂度\(O(n\log^2n)\)
注意对于一个扫把树,由于上来就处理深度最大的子树,后续子树都要付出一个很大代价。
需要按照最大深度从小到大访问子树,才能保证复杂度正确。
卡常严重,需要提前预处理好一些东西
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define maxn 100010
#define re register
#define eps 1e-4
#define inf 99999999
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
int x=0;char c=getchar();while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct son{int A,B;}st[maxn];
struct E{int v,nxt;double w;}e[maxn<<1];
int X[maxn],Y[maxn],V[maxn],head[maxn],sum[maxn],mx[maxn],vis[maxn],t[maxn],q[maxn];
double d[maxn],g[maxn],h[maxn];
int n,S,num,m,tot,now,rt,l,r,md,L,mt;
std::vector<int> v[maxn],u[maxn];
double ans;
inline int cmp(son a,son b) {return a.A<b.A;}
inline void add(int x,int y,double z) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;e[num].w=z;}
void getroot(int x,int fa) {
sum[x]=1,mx[x]=0;
for(re int i=head[x];i;i=e[i].nxt) {
if(e[i].v==fa||vis[e[i].v]) continue;
getroot(e[i].v,x);
sum[x]+=sum[e[i].v];mx[x]=max(mx[x],sum[e[i].v]);
}
mx[x]=max(mx[x],S-sum[x]);
if(mx[x]<now) now=mx[x],rt=x;
}
void getdis(int x,int fa,double len,int sz) {
d[++tot]=len,t[tot]=sz;
for(re int i=head[x];i;i=e[i].nxt) {
if(e[i].v==fa||vis[e[i].v]) continue;
getdis(e[i].v,x,len+e[i].w,sz+1);
}
}
void getmdep(int x,int fa,int sz) {
if(sz>mt) mt=sz;
for(re int i=head[x];i;i=e[i].nxt) {
if(e[i].v==fa||vis[e[i].v]) continue;
getmdep(e[i].v,x,sz+1);
}
}
void find(int x) {
vis[x]=1;int top=0;
for(re int i=head[x];i;i=e[i].nxt) {
if(vis[e[i].v]) continue;
mt=0;getmdep(e[i].v,0,1);st[++top].A=mt,st[top].B=i;
}
std::sort(st+1,st+top+1,cmp);
for(re int i=1;i<=top;i++) v[x].push_back(st[i].B);
for(re int k=0;k<v[x].size();k++) {
re int i=v[x][k];
if(vis[e[i].v]) continue;
S=sum[e[i].v],now=inf,getroot(e[i].v,0),u[x].push_back(rt),find(rt);
}
}
void dfs(int x) {
vis[x]=1;int cur=0,top=0;mt=0;
for(re int k=0;k<v[x].size();k++) {
re int i=v[x][k];
if(vis[e[i].v]) continue;
md=0;tot=0;cur++;getdis(e[i].v,0,e[i].w,1);
for(re int j=1;j<=tot;j++) {
g[t[j]]=max(g[t[j]],d[j]);md=max(md,t[j]);
if(t[j]>=l&&t[j]<=r) ans=max(ans,d[j]);
}
if(cur==1) {
mt=max(mt,md);
for(re int j=1;j<=md;j++) h[j]=g[j];
for(re int j=1;j<=tot;j++) g[t[j]]=-inf;
continue;
}
int head=1,tail=0,U=min(r-md,mt);
for(re int j=max(1,l-md);j<=U;j++) {
while(head<=tail&&h[j]>h[q[tail]]) tail--;
q[++tail]=j;
}
if(head<=tail) ans=max(ans,g[md]+h[q[head]]);
int lp=l-md,rp=r-md;
for(re int j=md-1;j>=1;j--) {
rp++,lp++;
while(head<=tail&&h[rp]>h[q[tail]]) tail--;
q[++tail]=rp;
while(head<=tail&&q[head]<lp) head++;
if(head<=tail) ans=max(ans,g[j]+h[q[head]]);
}
mt=max(mt,md);
for(re int j=1;j<=md;j++) h[j]=max(h[j],g[j]);
for(re int j=1;j<=md;j++) g[j]=-inf;
}
for(re int i=1;i<=mt;i++) h[i]=-inf;
for(re int k=0;k<v[x].size();k++) {
re int i=v[x][k];
if(vis[e[i].v]) continue;
dfs(u[x][k]);
}
}
inline int check(double mid) {
num=0;memset(head,0,sizeof(head));memset(vis,0,sizeof(vis));
for(re int i=1;i<n;i++)
add(X[i],Y[i],double(V[i])-mid),add(Y[i],X[i],double(V[i])-mid);
S=n,now=inf,ans=-inf,getroot(1,0);
dfs(rt);
return ans>0;
}
int main() {
n=read(),l=read(),r=read();
for(re int i=1;i<n;i++) X[i]=read(),Y[i]=read(),V[i]=read();
double L=0,R=900000.0;
memset(h,-20,sizeof(h));memset(g,-20,sizeof(g));
for(re int i=1;i<n;i++) add(X[i],Y[i],0),add(Y[i],X[i],0);
S=n,now=inf,ans=-inf,getroot(1,0);find(rt);
while(R-L>eps) {
double mid=(L+R)/2.0;
if(check(mid)) L=mid;else R=mid;
}
printf("%.3lf\n",L);
return 0;
}