【bzoj1758】 Wc2010—重建计划

http://www.lydsy.com/JudgeOnline/problem.php?id=1758 (题目链接)

题意

  给出一棵树,每条边有边权,问选出一条长度为$[L,U]$的路径,使得路径上的边权平均数最大是多少。

Solution

  哈哈,爸爸终于过啦。

  首先二分答案,然后路径统计显然点分治,统计答案的时候单调队列维护一下滑动窗口里面的最值。因为要点分治若干次,我们不妨将重心预处理出来,减少常数。

  一定要小心,在点分治处理子树的时候,一定要按照深度从小到大的顺序处理,不然直接被新加的那组扫把型的数据卡掉T_T。

细节

  清空cnt。

代码

// bzoj1758
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define LD long double
#define eps 1e-4
#define inf (1ll<<60)
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std;

const int maxn=100010;
int n,m,U,L,cnt,head[maxn];
struct edge {int to,next,w;}e[maxn<<1];
struct data {
	int num,D;
	friend bool operator < (data a,data b) {return a.D>b.D;}
};

namespace NodeDivide {
	int Dargen,rt,D,maxD,sum,size[maxn],vis[maxn],Sum[maxn],f[maxn],q[maxn],h[maxn];
	edge t[maxn];
	double ans,deep[maxn],dis[maxn],Dis[maxn];
	void link(int u,int v) {t[++cnt]=(edge){v,h[u]};h[u]=cnt;}
	void dfs(int x,int fa) {
		size[x]=f[x]=1;
		for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa && !vis[e[i].to]) {
				dfs(e[i].to,x);
				f[x]=max(f[x],size[e[i].to]);
				size[x]+=size[e[i].to];
			}
		f[x]=max(f[x],sum-size[x]);
		if (f[x]<f[rt]) rt=x;
	}
	void caldeep(int x,int fa,int d) {
		D=max(D,d);
		for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa && !vis[e[i].to]) {
				deep[e[i].to]=deep[x]+e[i].w;
				caldeep(e[i].to,x,d+1);
			}
	}
	void caldis(int x,int fa,int d) {
		dis[d]=max(dis[d],deep[x]);
		for (int i=head[x];i;i=e[i].next)
			if (e[i].to!=fa && !vis[e[i].to]) caldis(e[i].to,x,d+1);
	}
	void solve(int x,double res) {
		vis[x]=1;maxD=0;
		priority_queue<data> T;
		for (int i=head[x];i;i=e[i].next) if (!vis[e[i].to]) {
				D=0;deep[e[i].to]=e[i].w;
				caldeep(e[i].to,x,1);
				T.push((data){e[i].to,D});
			}
		while (!T.empty()) {
			data tmp=T.top();T.pop();
			caldis(tmp.num,x,1);
			int l=1,r=0,pl=maxD,pr=maxD;
			for (int i=1;i<=tmp.D;i++) {
				for (;pl>-1 && pl>=L-i;pl--) {
					while (l<=r && Dis[q[r]]-q[r]*res<Dis[pl]-pl*res) r--;
					q[++r]=pl;
				}
				for (;pr>-1 && pr>U-i;pr--) while (l<=r && q[l]>=pr) l++;
				if (pl<pr) ans=max(ans,dis[i]-i*res+Dis[q[l]]-q[l]*res);
			}
			maxD=max(maxD,tmp.D);
			for (int i=1;i<=tmp.D;i++) Dis[i]=max(Dis[i],dis[i]),dis[i]=0;
		}
		for (int i=1;i<=maxD;i++) Dis[i]=0;
		for (int i=h[x];i;i=t[i].next) if (Sum[t[i].to]>=L) solve(t[i].to,res);
		vis[x]=0;
	}
	void caldargen(int x,int fa) {
		vis[x]=1;
		if (fa) link(fa,x);else Dargen=x;
		for (int i=head[x];i;i=e[i].next) if (!vis[e[i].to]) {
				sum=size[e[i].to],rt=0;
				dfs(e[i].to,x);
				Sum[rt]=sum;
				caldargen(rt,x);
			}
		vis[x]=0;
	}
	void Init() {
		cnt=0;
		sum=n;f[rt=0]=1<<30;
		dfs(1,0);Sum[rt]=sum;
		caldargen(rt,0);
	}
	double main(double res) {
		ans=-inf;
		solve(Dargen,res);
		return ans;
	}
}
using namespace NodeDivide;

void link(int u,int v,int w) {
	e[++cnt]=(edge){v,head[u],w};head[u]=cnt;
	e[++cnt]=(edge){u,head[v],w};head[v]=cnt;
}
int main() {
	scanf("%d%d%d",&n,&L,&U);
	for (int u,v,w,i=1;i<n;i++) {
		scanf("%d%d%d",&u,&v,&w);
		link(u,v,w);
	}
	Init();
	double l=0,r=1000000,ans;
	while (l<=r) {
		double mid=(l+r)/2;
		if (main(mid)>=0) l=mid+eps,ans=mid;
		else r=mid-eps;
	}
	printf("%.3lf",ans);
	return 0;
}

 

posted @ 2017-03-06 15:13  MashiroSky  阅读(...)  评论(...编辑  收藏