1758: [Wc2010]重建计划

//被卡常了
#pragma GCC optimize("O2")
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define sqr(x) ((x)*(x))
#define G getchar()
#define LL long long
#define pll pair<LL,LL>
#define mkp make_pair
#define X first
#define Y second
#define N 100005
#define NN 10000000
#define inf 100000000001.0
#define eps 0.000001
int n,MIN,MAX;
int he[N],ne[N<<1],to[N<<1],W[N<<1],tot;
int sz[N],mx[N],rt[N],rk[N],cnt,ln[N];
int q[N],inq[N],dep[N],md1,md0;double sum[N],Max0[N],Max[N];
//dep表示深度,sum[I]表示根到i的边权和
//Max0[i]表示已经搜过的子树中,dep为i的点的sum的最大值。md0表示其最大下标
//Max表示当前正在搜的子树中,dep为I的点的sum最大值。md1表示其最大下标
//inq表示是否在队列中,等于cnt表示在,否则表示不在
inline int read(){
	int x=0;char ch=G;
	while(x>57||ch<48)ch=G;
	for(;ch<58&&ch>47;ch=G)x=x*10+ch-48;
	return x;
}
inline void add(int x,int y,int z){
	to[++tot]=y;W[tot]=z;ne[tot]=he[x];he[x]=tot;
}
void getrt(int x,int e){
	int i,y;mx[x]=0;sz[x]=1;//mx记录该节点的最大子树大小,sz表示以自己为根的子树大小 
	for(i=he[x];i;i=ne[i])if(i!=e&&!rk[y=to[i]]){
		getrt(y,i^1);mx[x]=max(mx[x],sz[y]);sz[x]+=sz[y];
	}
	if((mx[x]=max(mx[x],ln[cnt]-sz[x]))<mx[rt[cnt]])rt[cnt]=x;
}
inline int dcmp(double x){
	if(fabs(x)<eps)return 0;
	return x>0?1:-1;
}
inline bool BFS(int x,double ans){
	int u,v,Ft=1,Rr=2,i,j;bool rtn=0;
	//预处理Max
	for(inq[q[1]=x]=cnt;Ft<Rr;){
		u=q[Ft++];Max[dep[u]]=max(Max[dep[u]],sum[u]);
		for(i=he[u];i;i=ne[i])if(rk[v=to[i]]>cnt&&inq[v]<cnt){
			inq[q[Rr++]=v]=cnt;dep[v]=dep[u]+1;sum[v]=sum[u]+W[i]-ans;
		}
	}
	md1=dep[q[Rr-1]];j=md0;Ft=Rr=1;
	//MIN-I和MAX-I可看成一个滑动窗口,因此维护Max0的单调递减队列
	rep(i,MIN-md0,md1){
		while(Ft<Rr&&i+q[Ft]>MAX)Ft++;
		while(j&&i+j>=MIN){
			while(Ft<Rr&&dcmp(Max0[j]-Max0[q[Rr-1]])>=0)Rr--;
			q[Rr++]=j--;
		}
		if(Ft<Rr&&dcmp(Max0[q[Ft]]+Max[i])>=0){
			rtn=1;break;
		}
	}
	md0=max(md0,md1);
	rep(i,1,md1)Max0[i]=max(Max0[i],Max[i]);
	return rtn;
}
inline bool check(double ans){ 
	int i,j,x,y,rg; 
	rep(i,1,n)Max[i]=Max0[i]=-inf,inq[i]=0;
	//随时保证Max和Max0的初始值为-inf。Max0遍历每个根时都盖掉,Max遍历每棵子树时都改掉。
	rep(cnt,1,n){
		if(ln[cnt]<=MIN)continue; 
		dep[x=rt[cnt]]=0;
		rep(i,1,md0)Max0[i]=-inf;md0=0;
		for(i=he[x];i;i=ne[i])if(rk[y=to[i]]>cnt){
			rep(j,1,md1)Max[j]=-inf;md1=0;
			sum[y]=W[i]-ans;dep[y]=1;
			if(BFS(y,ans))return 1;
		}
		rg=min(MAX,md0);
		rep(i,MIN,rg)if(dcmp(Max0[i])>=0)return 1;
	}
	return 0;
}
int main(){
	freopen("r.in","r",stdin);
	freopen("w.out","w",stdout);
	//读入
	int i,x,y,z,j,rr=1;double l,r,mid;
	n=read();MIN=read();MAX=read();tot=1;
	rep(i,2,n){
		x=read();y=read();z=read();
		rr=max(z,rr);add(x,y,z);add(y,x,z);
	}
	//提前把每棵树的重心都找出来,rt[i]表示第i个搜谁,rk[i]表示第i个什么时候搜
	mx[n+1]=rt[cnt=1]=n+1;ln[1]=n;getrt(1,0);
	rep(i,1,n){
		rk[rt[i]]=i;
		for(j=he[rt[i]];j;j=ne[j])if(!rk[y=to[j]]){
			rt[++cnt]=n+1;ln[cnt]=sz[y];getrt(y,0);
		}
	}
	//实数二分
	for(l=1,r=rr;dcmp(r-l);){
		mid=(l+r)/2;
		if(check(mid))l=mid;
		else r=mid;
	}
	printf("%.3lf\n",l);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值