[Wc2010]重建计划 (分数规划二分+长链剖分+线段树)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/C20180602_csq/article/details/102312635

1758: [Wc2010]重建计划

Time Limit: 40 Sec  Memory Limit: 162 MB

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<=U<=N-1,Vi<=1000000

题解

这道题的要求是平均值最大,于是就想到了分数规划

分数规划详见大佬博客:https://www.cnblogs.com/captain1/p/9929128.html

感觉有点像带权二分(雾)

分数规划就是直接二分一下答案,然后把分母乘过来,移一下项,就会发现每一项都会减去一个对应的值

我们只需要把每一个点都赋上一个对应的值来进行验证答案就好啦

至于这道题,把|S|乘过来,再移项过去,就可以发现每一条边只需要赋为(长度-1*二分的答案),就可以求它们的最大值

来判断这个最大值是否为0

剩下的就是长链剖分+线段树的板子了

有一个小小的疑问,既然我们可以直接求在[L,U]限制下的最大值,为什么不直接求答案呢?

其实我们现在求的最大值只是保证了分子最大,而分母的值是不确定的,所以还是得二分答案。。。

还有一种比二分快10倍的方法,叫做迭代,详见大佬博客:https://blog.csdn.net/C20181220_xiang_m_y/article/details/102211422

下面具体讲一下长链剖分

其实长链剖分就是把自己的重儿子设置为拥有最深叶子节点的儿子。(类比重链剖分的重儿子设置的是子树节点最多的儿子)

然后类似于重链剖分

把整棵树的优先重儿子的dfs序求出来

这样一条链上的点就可以在线段树上对应一段连续的区间

如何求在[L,U]限制下的边权和最大值?

我们先存一个val[i],表示i节点到根的边权和

类似于dsu on tree的思路

先计算重儿子对答案的贡献(注意判断是否出界)

然后一个个计算轻儿子对答案的贡献

这时,轻儿子已经完成自己子树下面的计算,轻儿子的所有答案都已经合并到了轻儿子的长链上

所以就只用合并当前长链与轻儿子长链的maxval值就好了

总共O(nlogn),加上二分O(nlognlogn)

人生第一道长链剖分

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0;
	while((c=getchar())<'0'||c>'9');
	do{num=num*10+c-48;c=getchar();}while(c>='0'&&c<='9');
	return num;
}
int n,L,R;
#define LL long long
const LL INF=0x3f3f3f3f3f3f3f3fll;
const int EXP=10000;
#define N 100005
int fir[N],to[2*N],nxt[2*N],cnt;
LL cd[2*N];
void adde(int a,int b,LL c)
{
	to[++cnt]=b;cd[cnt]=c;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;cd[cnt]=c;nxt[cnt]=fir[b];fir[b]=cnt;
}
int dep[N],hei[N],son[N],fa[N];
LL val[N];
void dfs1(int u)
{
	hei[u]=dep[u]=dep[fa[u]]+1;
	int v,p;
	for(p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(v!=fa[u]){
			fa[v]=u;val[v]=val[u]+cd[p];
			dfs1(v);
			hei[u]=max(hei[u],hei[v]);
			if(hei[son[u]]<hei[v])
				son[u]=v;
		}
	}
}
int pos[N],ind[N],dfn;
void dfs2(int u)
{
	pos[u]=(++dfn);ind[dfn]=u;
	if(son[u]) dfs2(son[u]);
	int v,p;
	for(p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(v!=fa[u]&&v!=son[u])
			dfs2(v);
	}
}
#define lc i<<1
#define rc i<<1|1
struct node{
	int l,r;
	LL mx;
}a[N<<2];
void build(int i,int l,int r)
{
	a[i].l=l;a[i].r=r;a[i].mx=-INF;
	if(l==r){a[i].mx=val[ind[l]];return;}
	int mid=(l+r)>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	a[i].mx=max(a[lc].mx,a[rc].mx);
}
void insert(int i,int x,LL k)
{
	if(x<a[i].l||x>a[i].r) return;
	if(a[i].l==x&&x==a[i].r){
		a[i].mx=max(a[i].mx,k);
		return;
	}
	insert(lc,x,k);
	insert(rc,x,k);
	a[i].mx=max(a[lc].mx,a[rc].mx);
}
LL query(int i,int l,int r)
{
	if(r<a[i].l||a[i].r<l) return -INF;
	if(l<=a[i].l&&a[i].r<=r)
		return a[i].mx;
	return max(query(lc,l,r),query(rc,l,r));
}
LL ans;
void solve(int u)
{
	if(!son[u]) return;
	int l=pos[u],r=pos[u]+hei[u]-dep[u],v,p,_L,_R,d,s;
	solve(son[u]);
	if(l+L<=r)
		ans=max(ans,query(1,l+L,min(l+R,r))-val[u]);
	for(p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(v!=fa[u]&&v!=son[u]){
			solve(v);
			d=v;
			while(d){
				_L=L-(dep[d]-dep[v])-1,_R=R-(dep[d]-dep[v])-1;
				if(l+_L<=r)
					ans=max(ans,query(1,max(l+_L,l),min(l+_R,r))+val[d]-2*val[u]);
				d=son[d];
			}
			d=v;s=son[u];
			while(d){
				insert(1,pos[s],val[d]);
				val[s]=max(val[s],val[d]);
				d=son[d];s=son[s];
			}
		}
	}
}
bool check(LL mid)
{
	for(int i=1;i<=cnt;i++)
		cd[i]-=mid;
	dfn=0;dfs1(1);dfs2(1);build(1,1,n);
	ans=-INF;solve(1);
	for(int i=1;i<=cnt;i++)
		cd[i]+=mid;
	return ans>=0;
}
int main()
{
	int i,u,v,w;
	LL l,r=0,mid;
	n=gi();L=gi();R=gi();
	for(i=1;i<n;i++){
		u=gi();v=gi();w=gi();
		adde(u,v,1ll*w*EXP);
		r=max(r,1ll*w);
	}
	l=0;r=1ll*r*EXP;mid=(l+r)>>1;
	while(l<r-3){
		if(check(mid))
			l=mid;
		else
			r=mid-1;
		mid=(l+r)>>1;
	}
	for(mid=r;mid>=l;mid--){
		if(check(mid)){
			printf("%.3f",1.0*mid/EXP);
			return 0;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/C20180602_csq/article/details/102312635