COGS2652 秘术(天文密葬法)(分数规划+长链剖分)

传送门
题意: n n n个点的树,每个点两个值 a , b a,b a,b,问长度为 m m m的路径 ∑ a i ∑ b i \frac{\sum a_i}{\sum b_i} biai的最大值。


思路:一眼要01分数规划,考虑 c h e c k check check可以用点分治水掉。
然而也可以用长链剖分,复杂度降低一个 l o g log log
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
typedef long long ll;
const int N=1e5+5;
int mdep[N],dep[N],top[N],hson[N],len[N],fa[N],n,m,a[N],b[N];
vector<int>e[N];
double ans,w[N],ftmp[N<<1],*f[N],*tmp;
void dfs1(int p){
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa[p])continue;
		fa[v]=p,mdep[v]=dep[v]=dep[p]+1,dfs1(v),mdep[p]=max(mdep[p],mdep[v]);
		if(mdep[hson[p]]<mdep[v])hson[p]=v;
	}
	len[p]=mdep[p]-dep[p]+1;
}
void dfs2(int p){
	if(hson[p])f[hson[p]]=f[p]+1,dfs2(hson[p]);
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa[p]||v==hson[p])continue;
		f[v]=tmp,tmp+=len[v],dfs2(v);
	}
}
void dfs3(int p){
	f[p][0]=(w[p]+=w[fa[p]]);
	if(!hson[p])return;
	dfs3(hson[p]);
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa[p]||v==hson[p])continue;
		dfs3(v);
		for(ri j=0;j<len[v];++j)if(j+1+len[p]>m&&m>j)ans=min(ans,f[p][m-j-1]+f[v][j]-(w[p]+w[fa[p]]));
		for(ri j=0;j<len[v];++j)f[p][j+1]=min(f[p][j+1],f[v][j]);
	}
	if(len[p]>m)ans=min(ans,f[p][m]);
}
int main(){
	n=read(),m=read();
	for(ri i=1;i<=n;++i)a[i]=read();
	for(ri i=1;i<=n;++i)b[i]=read();
	for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
    if(m<0){for(int i=1;i<=n;++i)ans=min(ans,(double)a[i]/b[i]);return printf("%.2lf\n",ans),0;}
	dfs1(1);
	tmp=ftmp,f[1]=tmp,tmp+=mdep[1];
	dfs2(1);
	--m;
	double l=0.0,r=1e9;
	while(r-l>=1e-3){
		double mid=(l+r)/2;
		ans=1e18;
		for(ri i=1;i<=n;++i)w[i]=(double)a[i]-mid*b[i];
		dfs3(1);
		ans<=0?r=mid:l=mid;
	}
	printf("%.2lf",l);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值