tree

17 篇文章 0 订阅
7 篇文章 0 订阅

Description

给你一棵n个点的树,你需要在树上选择恰好m条点不相交的,点数至少为k的路径,使得路径所覆盖的点权和尽可能大。求最大点权和。数据保证有解。
n<=1.5e5

凸优化+长链剖分 时间复杂度O(nlogval)
存档题

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<assert.h>
#define fo(i,a,b) for(int i=(a);i<=(b);++i)
#define fd(i,b,a) for(int i=(b);i>=(a);--i)
#define bfo(i,v,u) for(int i=BB[v],u=B[i][1];i;u=B[i=B[i][0]][1])
#define mset(a,x) memset(a,x,sizeof(a))
#define mcpy(a,b) memcpy(a,b,sizeof(b))
template<typename T> bool chkmin(T &a,const T &b) {return b<a?a=b,1:0;}
template<typename T> bool chkmax(T &a,const T &b) {return b>a?a=b,1:0;}
using namespace std;
typedef long long ll;
int read(){int n=0,p=1;char ch;for(ch=getchar();ch<'0' || ch>'9';ch=getchar())if(ch=='-') p=-1;for(;'0'<=ch && ch<='9';ch=getchar()) n=n*10+ch-'0';return n*p;}
const int N=3e5+5;
const ll INF=1e12;
int n,m,k,now,cson[N],st[N],len[N],B0,BB[N],B[N<<1][2];
void link(int u,int v){B[++B0][1]=v,B[B0][0]=BB[u],BB[u]=B0;}
void dfs1(int v,int fr=0)
{
	len[v]=1;
	bfo(i,v,u) if(u!=fr)
	{
		dfs1(u,v);
		if(chkmax(len[v],len[u]+1)) cson[v]=u;
	}
}
void dfs2(int v,int fr=0)
{
	st[v]=++now;//g[v][j]==g[st[v]+j]
	if(cson[v]) dfs2(cson[v],v);
	++now;
	bfo(i,v,u) if(u!=fr && u!=cson[v])
		dfs2(u,v);
}
ll a[N];
struct info
{
	ll x;int y;
	friend info operator + (const info &a,const info &b) {return (info){a.x+b.x,a.y+b.y};}
	friend info operator - (const info &a,const info &b) {return (info){a.x-b.x,a.y-b.y};}
	friend info operator - (const info &a) {return (info){-a.x,-a.y};}
	friend bool operator > (const info &a,const info &b) {return a.x!=b.x ? a.x>b.x : a.y<b.y;}
	friend bool operator < (const info &a,const info &b) {return !(a > b);}
}s[N],f[N],g[N],tag[N];
void dp(int v,int fr,ll z)
{
	bfo(i,v,u) if(u!=fr)
	{
		dp(u,v,z);
		s[v] = s[v] + f[u];
	}
	f[v] = max(f[v], s[v]);
	info d = s[v] + (info){a[v],0};
	tag[v] = tag[cson[v]] + d;
	g[st[v]] = max(tag[v]+g[st[v]+1], d) - tag[v];
	if(len[v]>=k) f[v] = max(f[v], tag[v] + g[st[v]+k-1] + (info){z,1});
	bfo(i,v,u) if(u!=fr && u!=cson[v])
	{
		info t;
		fo(j,1,len[u])
		{
			t = tag[u] + g[st[u]+j-1] + tag[v] + g[st[v]+max(k-j,1)-1];
			t = t + (info){z,1};
			if(k-j <= len[v]) chkmax(f[v], t);
		}
		t = (info){-INF,0};
		fd(j,len[u],1)
		{
			chkmax(t, tag[u] + g[st[u]+j-1] + d);
			if(t > tag[v] + g[st[v]+j]) g[st[v]+j] = t - tag[v];
		}
		if(t > tag[v] + g[st[v]]) g[st[v]] = t - tag[v];
	}
	tag[v] = tag[v] - f[v];
}
void check(ll z)
{
	fo(i,1,n)
		s[i] = f[i] = g[i] = tag[i] = (info){0,0};
	dp(1,0,z);
}
int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	int x,y;
	n=read(),m=read(),k=read();
	fo(i,1,n) a[i]=read();
	fo(i,1,n-1) x=read(),y=read(),link(x,y),link(y,x);
	dfs1(1);
	dfs2(1);
	ll l=-INF,r=INF+1;
	while(l<r-1)
	{
		ll mid=(l+r)>>1;
		check(mid);
		if(f[1].y==m)
		{
			printf("%lld\n",f[1].x-m*mid);
			return 0;
		}
		if(f[1].y<m) l=mid;
		else r=mid;
	}
	check(l);
	printf("%lld\n",f[1].x-m*l);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值