[BZOJ1758][WC2010]重建计划

dbzoj
luogu

sol

点分治+二分答案+单调队列。
点分,每次考虑所有过重心的路径,二分一个答案\(mid\),给每条边的边权减掉一个\(mid\)后,满足条件的路径应该是长度介于\(L\)\(U\)之间,且权值和大于等于\(0\)的。
\(bfs\)一遍求出每个点到重心的路径的长度\(dep\)和路径权值\(dis\)。由于是\(bfs\),所以\(dep\)一定是单调增的。
维护一个路径长度单调减而路径权值单调增的单调队列,由于待匹配路径的\(dep\)单调增,所以可以从大往小把原有路径加入单调队列,保证单调队列中的任意元素取出来和待匹配路径匹配都能满足大于等于\(L\)的条件。
每次与队首计算贡献,注意要满足长度之和小于等于\(U\)的条件。

code

#include<cstdio>
#include<algorithm>
using namespace std;
int gi()
{
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 1e5+5;
const double eps = 1e-4;
int n,L,U,to[N<<1],nxt[N<<1],ww[N<<1],head[N],cnt,Max;
int sz[N],f[N],sum,root,vis[N],mark[N],Q[N],H,T,dep[N],q[N];
double ans,dis[N],t[N];
void link(int u,int v,int w)
{
	to[++cnt]=v;nxt[cnt]=head[u];ww[cnt]=w;
	head[u]=cnt;
}
void getroot(int u,int fa)
{
	sz[u]=1;f[u]=0;
	for (int e=head[u];e;e=nxt[e])
		if (!vis[to[e]]&&to[e]!=fa)
		{
			getroot(to[e],u);
			sz[u]+=sz[to[e]];
			f[u]=max(f[u],sz[to[e]]);
		}
	f[u]=max(f[u],sum-sz[u]);
	if (f[u]<f[root]) root=u;
}
void bfs(int u,double mid)
{
	Q[H=T=1]=u;mark[u]=1;
	while (H<=T)
	{
		int u=Q[H++];
		for (int e=head[u];e;e=nxt[e])
			if (!vis[to[e]]&&!mark[to[e]])
			{
				dep[to[e]]=dep[u]+1,dis[to[e]]=dis[u]+(double)ww[e]-mid;
				Q[++T]=to[e];mark[to[e]]=1;
			}
	}
}
bool check(int u,double mid)
{
	int fg=0,maxd=0;
	for (int e=head[u];e;e=nxt[e])
		if (!vis[to[e]])
		{
			dis[to[e]]=(double)ww[e]-mid;dep[to[e]]=1;
			bfs(to[e],mid);
			int hd=1,tl=0,j=maxd;
			for (int i=1;i<=T;++i)
			{
				while (j>=0&&j+dep[Q[i]]>=L)
				{
					while (hd<=tl&&t[q[tl]]<t[j]) --tl;
					q[++tl]=j;--j;
				}
				while (hd<=tl&&dep[Q[i]]+q[hd]>U) ++hd;
				if (hd<=tl&&dis[Q[i]]+t[q[hd]]>=0) fg=1;
			}
			maxd=max(maxd,dep[Q[T]]);
			for (int i=1;i<=T;++i)
			{
				mark[Q[i]]=0;
				t[dep[Q[i]]]=max(t[dep[Q[i]]],dis[Q[i]]);
			}
		}
	for (int i=1;i<=maxd;++i) t[i]=-1e12;
	return fg;
}
void calc(int u)
{
	double l=ans,r=Max;
	while (r-l>eps)
	{
		double mid=(l+r)/2;
		if (check(u,mid)) l=mid;
		else r=mid;
	}
	ans=l;
}
void solve(int u)
{
	vis[u]=1;calc(u);
	for (int e=head[u];e;e=nxt[e])
		if (!vis[to[e]])
		{
			sum=sz[to[e]];root=0;
			getroot(to[e],0);
			solve(root);
		}
}
int main()
{
	n=gi();L=gi();U=gi();
	for (int i=1;i<n;++i)
	{
		int u=gi(),v=gi(),w=gi();
		link(u,v,w);link(v,u,w);Max=max(Max,w);
	}
	sum=f[0]=n;
	getroot(1,0);
	solve(root);
	printf("%.3lf\n",ans);
	return 0;
}
posted @ 2018-04-03 09:27  租酥雨  阅读(162)  评论(1编辑  收藏  举报