[BZOJ1758/WC2010]重建计划

Description

Input
第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数\(A_i,B_i,V_i\)分别表示道路\((A_i,B_i)\),其价值为\(V_i\) 其中城市由1..N进行标号

Output
输出最大平均估值,保留三位小数

Sample Input
4
2 3
1 2 1
1 3 2
1 4 3

Sample Output
2.500

HINT
\(N\leqslant 100000,1\leqslant L\leqslant U\leqslant N-1,V_i\leqslant 1000000\)


考虑二分答案,然后把边权减去二分出的答案,判断是否存在>0的路径即可

点分治合并路径的时候记得按秩合并,即将子树按最深深度排序后枚举,开一个单调队列,统计完一棵子树后累计答案

二分有好几个位置,最外面、点分时、枚举子树时,放最外面常数最大(然而我就是这么写的),放在中间的位置可以随时改变二分上下界

为了卡常,可以先把点分治的重心枚举顺序先弄出来,然后类似for循环去点分治(因为luogu单点时限,Bzoj不需要)

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
	static char buf[1000000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
	int x=0,f=1; char ch=gc();
	for (;ch<'0'||ch>'9';ch=gc())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=gc())	x=(x<<3)+(x<<1)+ch-'0';
	return x*f;
}
inline int read(){
	int x=0,f=1; char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<3)+(x<<1)+ch-'0';
	return x*f;
}
inline void print(int x){
	if (x<0)	putchar('-'),x=-x;
	if (x>9)	print(x/10);
	putchar(x%10+'0');
}
const int N=1e5;
const double eps=1e-10,unit=1e-4;
int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],val[(N<<1)+10];
int size[N+10],Df[N+10],Root[N+10];
bool vis[N+10],flag;
double DV[N+10],Del;
int root,Max,r_sz;
int n,L,R,tot;
void join(int x,int y,int z){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z;}
void insert(int x,int y,int z){join(x,y,z),join(y,x,z);}
void Get_root(int x,int fa,int sz){
	int res=0; size[x]=1;
	for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
		if (son==fa||vis[son])	continue;
		Get_root(son,x,sz);
		size[x]+=size[son];
		res=max(res,size[son]);
	}
	res=max(res,sz-size[x]);
	if (res<Max)	Max=res,root=x;
}
void get_Df(int x,int fa,int deep){
	Df[x]=deep;
	for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
		if (son==fa||vis[son])	continue;
		get_Df(son,x,deep+1);
		Df[x]=max(Df[x],Df[son]);
	}
}
void get_dis(int x,int fa,double v,int Dp){
	if (Dp>R)	return;
	DV[Dp]=max(DV[Dp],v);
	for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
		if (son==fa||vis[son])	continue;
		get_dis(son,x,v+val[p]-Del,Dp+1);
	}
}
struct S1{
	int x,Deep; double v;
	oid insert(int _x,int _D,double _v){x=_x,Deep=_D,v=_v;}
	bool operator <(const S1 &tis)const{return Deep<tis.Deep;}
}A[N+10];
double f[N+10];
void solve(double *a,int &la,double *b,int &lb){
	la=min(la,R),lb=min(lb,R);
	static int h[N+10]; int head=1,tail=0;
	for (int i=lb,j=0;i;i--){
		while (j<=la&&i+j<=R){
			while (head<=tail&&a[h[tail]]<=a[j])	tail--;
			h[++tail]=j++;
		}
		while (head<=tail&&h[head]+i<L)	head++;
		if (head>tail)	continue;
		if (a[h[head]]+b[i]>-eps){
			flag=1;
			return;
		}
	}
}
void merge(double *a,int &la,double *b,int &lb){for (int i=1;i<=max(la,lb);i++)	a[i]=max(a[i],b[i]),b[i]=-inf; la=lb;}
void divide(int len){
	if (len>n)	return;
	int x=Root[len];
	if (flag)	return;
	is[x]=1; int son_sz=0;//num of son
	for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
		if (vis[son])	continue;
		get_Df(son,0,1),Df[son]=min(Df[son],R);
		A[++son_sz].insert(son,Df[son],val[p]-Del);
	}
	int f_sz=0;
	sort(A+1,A+1+son_sz);
	if (A[son_sz].Deep<<1<L){
		divide(len+1);
		return;
	}
	for (int i=1;i<=son_sz;i++){
		get_dis(A[i].x,0,A[i].v,1);
		if (f_sz+A[i].Deep>=L){
			solve(f,f_sz,DV,A[i].Deep);
			if (flag)	return;
		}
		merge(f,f_sz,DV,A[i].Deep);
	}
	if (flag)	return;
	for (int i=1;i<=f_sz;i++)	f[i]=-inf;
	divide(len+1);
}
bool check(double v){
	Del=v,flag=0;
	memset(vis,0,sizeof(vis));
	for (int i=1;i<=R;i++)	DV[i]=f[i]=-inf;
	divide(1);
	return flag;
}
void Frt(int x){//Find root
	is[Root[++r_sz]=x]=1;
	for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
		if (vis[son])	continue;
		root=0,Max=inf;
		Get_root(son,0,size[son]);
		Frt(root);
	}
	is[x]=0;
}
int main(){
	n=read(),L=read(),R=read();
	double l=1e9,r=0;
	for (int i=1;i<n;i++){
		int x=read(),y=read(),z=read();
		insert(x,y,z);
		l=min(l,1.0*z);
		r=max(r,1.0*z);
	}
	root=0,Max=inf;
	Get_root(1,0,n);
	Frt(root);
	while (l<=r){
		double mid=(l+r)/2;
		if (check(mid))	l=mid+unit;
		else	r=mid-unit;
	}
	printf("%.3lf\n",r);
	return 0;
}
posted @ 2019-01-03 16:24  Wolfycz  阅读(189)  评论(0编辑  收藏  举报