码迷,mamicode.com
首页 > 其他好文 > 详细

Luogu4292 WC2010重建计划

时间:2020-07-27 23:34:24      阅读:65      评论:0      收藏:0      [点我收藏+]

标签:printf   eps   ons   sign   get   ref   就是   ++   通过   

Description

link

给定一个边带权的 \(n\) 点树,求出一条 \(length \in [L,R]\) 的路径,使得路径上面权值的均值最大

\(n \le 10^5\)

Solution

\[ans=\max\limits_{i=L}^R \frac {\sum_{p\in E} w_p} i [L\le|E|\le R] \]

我们显然地发现这个是个 \(01\) 分数规划问题

就是在若干个里面选择 \(l\le x \le r\) 个,最大化一个值

那么二分答案……(不会?右转百度搜索)

然后在判断的时候还是推那个式子

\(f_{i}=w_i-mid\times p[i],\) 在这里就是\(f_i=x_i-mid\)(边权减掉 \(mid\)

所以问题变成了是不是存在一条在长度限制下权值和大于 \(0\) 的路径,点分治做

所以后面的瓶颈在路径信息的合并

按照采药人的路径一题,我们可以计算出来之前的子树的信息存下来,然后暴力更新信息,这样显然复杂度是大于 \(O(n^2)\) 的做法,不能通过

在找到一个根的时候对它的子树进行 \(bfs\),这样保证了路径长度问题,然后我们统计下来到根的路径的价值和

用单调队列维护深度,使得 \(dis\) 的大小递减,如果出现满足条件的情况就返回即可

我因为到底是在外层写点分树还是二分瞎改了半天

然后在 \(bfs\) 子树的时候没有记录父节点干了一下午……

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
	inline int read()
	{
		int res=0,f=1; char k;
		while(!isdigit(k=getchar())) if(k==‘-‘) f=-1;
		while(isdigit(k)) res=res*10+k-‘0‘,k=getchar();
		return res*f;
	}
	const int N=2e5+10;
	const double eps=1e-4;
	int siz,minn,sz[N],rt,n,l,r,f[N],dep[N],q[N],q1[N],beg,fin;
	struct node{
		int to,nxt;
		double dis;
	}e[N<<1];
	int head[N],cnt; double dis[N],maxx[N];
	bool vis[N];
	inline void add(int u,int v,double w)
	{
		e[++cnt].to=v; e[cnt].dis=w; e[cnt].nxt=head[u];
		return head[u]=cnt,void();
	}//原树相关
	inline void getrt(int x,int fa)
	{
		sz[x]=1; int tmp=0;
		for(int i=head[x];i;i=e[i].nxt)
		{
			int t=e[i].to;
			if(vis[t]||t==fa) continue;
			getrt(t,x); sz[x]+=sz[t]; tmp=max(tmp,sz[t]);
		} tmp=max(tmp,siz-sz[x]);
		if(tmp<minn) rt=x,minn=tmp;
		return ;
	}
	int fa[N];
	inline bool check(int id,double x)
	{
		int md=0;
		for(int rec=head[id];rec;rec=e[rec].nxt)
		{
			int t=e[rec].to; if(vis[t]) continue;
			//bfs搞子树信息
			beg=0; fin=1; q[0]=t; fa[t]=id; dis[t]=e[rec].dis-x; dep[t]=1;
			while(beg!=fin)
			{
				int fr=q[beg]; ++beg;
				for(int i=head[fr];i;i=e[i].nxt)
				{
					int tt=e[i].to; if(vis[tt]||tt==fa[fr]) continue; fa[tt]=fr;
					dis[tt]=e[i].dis+dis[fr]-x; dep[tt]=dep[fr]+1; q[fin]=tt; fin++;
				}
			}
			//这里的q和fin和beg都是在bfs子树的时候的
			int st=1,ed=0,now=md;//now是原来深度的最大值
			for(int i=0;i<fin;++i)
			{
				int x=q[i]; 
				while(now>=0&&dep[x]+now>=l) 
				{
					while(st<=ed&&maxx[now]>maxx[q1[ed]]) --ed; 
					q1[++ed]=now; now--;//更新相关深度的信息
				}
				while(st<=ed&&dep[x]+q1[st]>r) ++st;
				if(st<=ed&&dis[x]+maxx[q1[st]]>=0) return 1;
			}//q1这里是单调递减的维护深度的序列,维护的是原来子树的信息,里面存的是深度值 
			for(int i=md+1;i<=dep[q[fin-1]];++i) maxx[i]=-1e18-10;
			for(int i=0;i<fin;++i)
			{
				int x=q[i];
				maxx[dep[x]]=max(maxx[dep[x]],dis[x]);
			} 
			//更新对应深度的最大值
			md=max(md,dep[q[fin-1]]);
		} return 0;
	}
	double ed,ans,st;
	inline void calc(int x)
	{
		double L=ans,R=ed;
		while(R-L>eps)
		{
			double mid=(L+R)/2;
			if(check(x,mid)) L=mid; else R=mid;
		} ans=L;
		return ;
	}
	inline void dfs(int x)
	{
		vis[x]=1; calc(x); 
		for(int i=head[x];i;i=e[i].nxt)
		{
			int t=e[i].to; if(vis[t]) continue;
			siz=sz[t]; minn=1e18+10; getrt(t,0); 
			if(sz[t]>l) dfs(rt);
		} return ;
	}
	signed main()
	{
		n=read(); l=read(); r=read();
		for(int i=1,u,v;i<n;++i)
		{
			double w;
			u=read(),v=read(),scanf("%lf",&w);
			add(u,v,w),add(v,u,w),ed=max(ed,w); st=min(st,w);
		} ans=st; minn=1e18+10; siz=n; getrt(1,0); dfs(rt); 
		printf("%.3lf\n",ans);
		return 0;
	}
}
signed main(){return yspm::main();}

Luogu4292 WC2010重建计划

标签:printf   eps   ons   sign   get   ref   就是   ++   通过   

原文地址:https://www.cnblogs.com/yspm/p/13386401.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!