【BZOJ 1758】 [Wc2010]重建计划

1758: [Wc2010]重建计划

Time Limit: 40 Sec  Memory Limit: 162 MB

Submit: 989  Solved: 345

[Submit][Status]

Description

Input

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

Output

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

Sample Input

4

2 3

1 2 1

1 3 2

1 4 3

Sample Output

2.500

HINT

20%的数据,N<=5000

30%的数据,N<=100000,原有方案恰好为一条路径

100%的数据,N<=100000,1<=L<=U<=N-1,Vi<=1000000

01分数规划+点分治。

做这道题的时候先看30%的数据,01分数规划+队列优化dp很好做。

那么对于一棵树呢?

用点分治来做就可以了!

01分数规划就是二分答案ans,然后把边权设为ans-原边权,若最小边权和小于0,那么增大ans,反之减小ans。

考虑经过根结点的(边权是更新过的)最小边权和:

枚举每一棵子树,用队列优化的dp来计算他与已经计算过的子树的满足>=l,<=u的最小边权和。

再递归处理每一个儿子。

于是整棵树的最小边权和就求出来了,若<=0,那么增大ans,反之减小ans。

直接这样写,TLE了5个点。

优化:

1.把二分写在点分治里面:

处理每一棵子树之前先二分求出经过当前树根的ans,在以后二分的时候把下界直接设成ans。

2.二分的时候根据优化1,可以感觉到最后的结果是偏向下界的,所以把mid=(l*19+r)/20

加上这些优化还是TLE了两个点TAT!!

求帮助。。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#define eps 1e-4
#define ld long double
#define M 100005
#define inf 0x3f3f3f3f
using namespace std;
bool f;
int tt=0;
int h[M],n,aa,aaa,tot,now,ti=0,s[M],l,u,ma[M],root,size,siz,done[M];
ld ans,Ans,a[M],b[M],c[M],mm=0.00;
struct edge
{
	int y,ne;
	ld v;
}e[M*3];
struct Q
{
	ld v;
	int p;
}q[M];
void Addedge(int x,int y,ld v)
{
	e[++tot].y=y;
	e[tot].v=v;
	e[tot].ne=h[x];
	h[x]=tot;
}
void read(int &tmp)
{
	tmp=0;
	int fu=1;
	char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar())
		if (ch=='-') fu=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())
		tmp=tmp*10+ch-'0';
	tmp*=fu;
}
void Getroot(int x,int fa)
{
	ma[x]=0,s[x]=1;
	for (int i=h[x];i;i=e[i].ne)
	{
		int y=e[i].y;
		if (y==fa||done[y]) continue;
		Getroot(y,x);
		s[x]+=s[y];
		if (ma[x]<s[y]) ma[x]=s[y];
	}
	if (ma[x]<siz-s[x]) ma[x]=siz-s[x];
	if (ma[x]<ma[root]) root=x;
}
int Getsize(int x,int fa)
{
	s[x]=1;
	for (int i=h[x];i;i=e[i].ne)
	{
		int y=e[i].y;
		if (done[y]||y==fa) continue;
		s[x]+=Getsize(y,x);
	}
	return s[x];
}
void dfs(ld k,int x,int fa,int dep)
{
	if (dep>u) return;
	for (int i=h[x];i;i=e[i].ne)
	{
		int y=e[i].y;
		if (done[y]||y==fa) continue;
		c[y]=c[x]+k-e[i].v;
		if (c[y]<b[dep]) b[dep]=c[y];
		dfs(k,y,x,dep+1);
	}
	if (dep>aa) aa=dep;
}
void dp()
{
	int ql=1,qr=0;
	for (int i=aaa;i>=l;i--)
	{
		while (ql<=qr&&a[i]<=q[qr].v)
			qr--;
		q[++qr].v=a[i],q[qr].p=i;
	}
	for (int i=0;i<=aa;i++)
	{
		if (q[ql].p==u-i+1) ql++;
		if (ql<=qr&&ans>q[ql].v+b[i]) ans=q[ql].v+b[i];
		if (i>=l) continue;
		while (ql<=qr&&a[l-i-1]<=q[qr].v)
			qr--;
		q[++qr].v=a[l-i-1],q[qr].p=l-i-1;
	}
}
ld Solve(int x,ld k)
{
	ans=inf;
	b[0]=0,a[0]=0;
	aaa=0,aa=0;
	for (int i=1;i<=u;i++)
		b[i]=a[i]=inf;
	for (int i=h[x];i;i=e[i].ne)
	{
		int y=e[i].y;
		if (done[y]) continue;
		b[1]=c[y]=k-e[i].v;
		aa=0;
		dfs(k,y,x,2);
		dp();
		if (ans<=0.00) {f=true;return ans;}
		for (int j=1;j<=u;j++)
			if (b[j]<a[j]) a[j]=b[j],b[j]=inf;
		if (aa>aaa) aaa=aa;
	}
	return ans;
}
void Work(int x,int size)
{
	if (size-1<l)
	{
		done[x]=1;
		return;
	}
	ld L=Ans,R=mm;
	while (R-L>eps)
	{
		ld mid=(L*19.0+R)/(20.0);
		if (Solve(x,mid)<=0) L=mid;
		else R=mid;
	}
	Ans=L;
	done[x]=1;
	for (int i=h[x];i;i=e[i].ne)
	{
		int y=e[i].y;
		if (done[y]) continue;
        siz=Getsize(y,0);
		root=0,ma[0]=inf;
		Getroot(y,0);
		Work(root,siz);
	}
}
int main()
{
        read(n),read(l),read(u);
	for (int i=1;i<n;i++)
	{
		int x,y,v;
		read(x),read(y),read(v);
		if ((ld)v>mm) mm=v;
		Addedge(x,y,(ld)v),Addedge(y,x,(ld)v);
	}
	for (int i=1;i<=n;i++)
		done[i]=0;
	siz=n;
	root=0;
	ma[0]=inf;
	Getroot(1,0);
	Ans=0.00;
	Work(root,n);
	printf("%.3Lf\n",Ans);
	return 0;
}

时间: 2024-08-26 18:39:03

【BZOJ 1758】 [Wc2010]重建计划的相关文章

bzoj 1758 [Wc2010]重建计划 分数规划+树分治单调队列check

[Wc2010]重建计划 Time Limit: 40 Sec  Memory Limit: 162 MBSubmit: 4345  Solved: 1054[Submit][Status][Discuss] Description Input 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai,Bi,Vi分别表示道路(Ai,Bi),其价值为Vi 其中城市由1..N

[BZOJ]1758: [Wc2010]重建计划

题目大意:给定一棵n个点的带边权的树和l,u,求长度在[l,u]之间平均权值最大的链的权值.(n<=100,000) 思路:二分答案,把树上每条边减去二分出的答案,点分治check是否有长度在[l,u]之间权值和大等0的链,每次把每棵子树按深度排序,记下各个深度到根距离最大的节点,再用单调队列统计即可,总复杂度O(nlogn^2).此题卡常差评,重心只要算一次,不用每次二分都算,稍微卡卡就过了. #include<cstdio> #include<cstring> #incl

BZOJ 1758 Wc2010 重建计划 树的点分治+二分+单调队列

题目大意:给定一棵树,询问长度在[l,u]范围内的路径中边权的平均值的最大值 01分数规划,首先想到二分答案 既然是统计路径肯定是点分治 每次统计时我们要找有没有大于0的路径存在 那么对于一棵子树的每一个深度i记录一个路径权值和的最大值 然后在这棵子树之前的所有子树的深度可选范围就是[l-i,u-i] 这个窗口是不停滑动的 因此用单调队列维护最大值即可 ↑上面这些网上的题解都说的还是蛮详细的 据说二分套在树分治里面会快一些 但是 尼玛 我被卡常了!! 这里只提供一些剪枝 1.当前分治的总点数<=

【bzoj1758】[Wc2010]重建计划

Description Input 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai,Bi,Vi分别表示道路(Ai,Bi),其价值为Vi 其中城市由1..N进行标号 Output 输出最大平均估值,保留三位小数 Sample Input 4 2 3 1 2 1 1 3 2 1 4 3 Sample Output 2.500 HINT N<=100000,1<=L

bzoj1758 [Wc2010]重建计划

Description Input 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai,Bi,Vi分别表示道路(Ai,Bi),其价值为Vi 其中城市由1..N进行标号 Output 输出最大平均估值,保留三位小数 Sample Input 4 2 3 1 2 1 1 3 2 1 4 3 Sample Output 2.500 HINT N<=100000,1<=L

bzoj1758 [Wc2010]重建计划 &amp; bzoj2599 [IOI2011]Race

两题都是树分治. 1758这题可以二分答案avgvalue,因为avgvalue=Σv(e)/s,因此二分后只需要判断Σv(e)-s*avgvalue是否大于等于0,若大于等于0则调整二分下界,否则调整二分上界.假设一棵树树根为x,要求就是经过树根x的最大答案,不经过树根x的可以递归求解.假设B[i]为当前做到的一颗x的子树中的点到x的距离为i的最大权值,A[i]为之前已经做过的所有子数中的点到x的距离为i的最大权值(这里的权值是Σv(e)-i*avgvalue),那么对于当前子树的一个距离i,

BZOJ1758: [Wc2010]重建计划(01分数规划+点分治+单调队列)

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1758 01分数规划,所以我们对每个重心进行二分.于是问题转化为Σw[e]-mid>=0, 对于一棵子树维护点的dep,dis,并用队列q存下来.令mx[i]表示当前dep为i的最大权值,维护一个单调队列dq,维护当前符合条件的mx,当我们从q的队尾向前扫时,它的dep是递减的,利用这个性质维护单调队列,最后更新一遍mx.具体看代码吧TAT 代码: #include<cstring>#

UOJ#54 [WC2010]重建计划

题目描述 小 X 驾驶着他的飞船准备穿梭过一个 \(n\) 维空间,这个空间里每个点的坐标可以用 \(n\) 个实数表示,即 \((x_1,x_2,\dots,x_n)\). 为了穿过这个空间,小 X 需要在这个空间中选取 \(c\)(\(c\geq 2\))个点作为飞船停留的地方,而这些点需要满足以下三个条件: 每个点的每一维坐标均为正整数,且第 \(i\) 维坐标不超过 \(m_i\). 第 \(i+1\)(\(1\leq i<c\))个点的第 \(j\)(\(1\leq j\leq n\)

P4292 [WC2010]重建计划

传送门 首先这玩意儿很明显是分数规划,二分一个答案\(mid\),边权变为\(w_i-mid\),然后看看能不能找到一条路径长度在\([L,R]\)之间,且边权总和非负,这个可以转化为求一条满足条件的边权最大的路径 这个实际上可以用点分做,用单调队列可以优化到\(O(nlog^2n)\),然而我不知道为什么写挂掉了所以这里就不说了-- 我们设\(f_{i,j}\)表示以\(i\)为根的子树中,从\(i\)向下走\(j\)步的链最长是多少.转移可以用长链剖分优化到均摊\(O(1)\).查询答案的话