题目大意:
给一棵边带权树,求一条长度在[L,R]中且平均权值最大的链。n<=100000,边权小于1000000.
解题思路:
先分数规划,把每条边边权减去mid,就变成了判断是否有一条长度在[L,R]的链权值大等于0.
考虑点分治,对于一个点 u u ,即是求一个和 不同子树的点 v v ,且 ,满足 dis[v]+dis[u]≥0 d i s [ v ] + d i s [ u ] ≥ 0
显然可以用线段树维护 dis d i s 最大值,但这样是 O(nlog3n) O ( n l o g 3 n ) 的,如何线性求出呢?
如果我们按深度从小到大枚举点 u u ,那么 的可行区间也在向右移动,就可以用单调队列维护了。
注意子树也要按最大深度从小到大枚举,不然会被卡成 O(n2logn) O ( n 2 l o g n )
还有二分写点分里面比较好,因为可以不停更新二分左边界。
ps:写完后TLE成zz,一个坑填了又有一个坑,上面的注意都是亲历,所以改了好几份代码……
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=200005;
const double eps=1e-9,INF=1e9;
int n,m,L,R,cnt,maxdep;
int tot,first[N],nxt[N<<1],to[N<<1];
int root,totsize,size[N],maxsub[N],vis[N];
int q[N],mx[N],V[N];
double f[N],g[N],w[N<<1],dis[N],ans,mine=INF,maxe;
inline bool cmp(const int &a,const int &b){return mx[a]<mx[b];}
void add(int x,int y,int z)
{
nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
}
bool findroot(int u,int fa)
{
size[u]=1,maxsub[u]=0;
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];if(v==fa||vis[v])continue;
findroot(v,u),size[u]+=size[v];
if(size[v]>maxsub[u])maxsub[u]=size[v];
}
maxsub[u]=max(maxsub[u],totsize-size[u]);
if(maxsub[u]<maxsub[root])root=u;
}
void dfs(int u,int fa,int dep,double d)
{
cnt=max(cnt,dep),dis[u]=d;
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];if(v==fa||vis[v])continue;
dfs(v,u,dep+1,d+w[e]);
}
}
void dfs(int u,int fa,int dep)
{
g[dep]=max(g[dep],dis[u]),cnt=max(cnt,dep);
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];if(v==fa||vis[v])continue;
dfs(v,u,dep+1);
}
}
bool check(double mid)
{
for(int i=1;i<=maxdep;i++)f[i]-=mid*i;
for(int i=1;i<=cnt;i++)g[i]-=mid*i;
int head=1,tail=0;bool res=false;
for(int i=cnt,j=0;i&&!res;i--)
{
while(j<=R-i&&j<=maxdep)
{
while(head<=tail&&f[q[tail]]<=f[j])tail--;
q[++tail]=j,j++;
}
while(head<=tail&&q[head]<L-i)head++;
if(head<=tail&&g[i]+f[q[head]]>=0)res=true;
}
for(int i=1;i<=maxdep;i++)f[i]+=mid*i;
for(int i=1;i<=cnt;i++)g[i]+=mid*i;
return res;
}
void solve(int u)
{
vis[u]=1;maxdep=m=dis[u]=0;
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];if(vis[v])continue;
V[++m]=v,cnt=0,dfs(v,u,1,w[e]),mx[v]=cnt;
}
sort(V+1,V+m+1,cmp);
for(int i=1;i<=m;i++)
{
int v=V[i];
cnt=mx[v],dfs(v,u,1);
double l=max(mine,ans),r=maxe;
while(r-l>eps)
{
double mid=(l+r)/2;
if(check(mid))l=mid;
else r=mid;
}
ans=r;
maxdep=max(maxdep,cnt);
for(int i=1;i<=cnt;i++)f[i]=max(f[i],g[i]),g[i]=-INF;
}
for(int i=1;i<=maxdep;i++)f[i]=-INF;
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];if(vis[v])continue;
root=0,maxsub[0]=totsize=size[v],findroot(v,u);
solve(root);
}
}
int main()
{
//freopen("lx.in","r",stdin);
//freopen("lx.out","w",stdout);
n=getint(),L=getint(),R=getint();
int x,y,z;
for(int i=1;i<n;i++)
{
x=getint(),y=getint(),z=getint();
add(x,y,z),add(y,x,z);
mine=min(mine,(double)z),maxe=max(maxe,(double)z);
}
for(int i=1;i<=n;i++)g[i]=f[i]=-INF,vis[i]=0;
root=0,maxsub[0]=totsize=n,findroot(1,0);
solve(root);
printf("%0.3lf\n",ans);
return 0;
}