【带权二分+树形DP】[九省联考 2018] 林克卡特树

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/87886162

update
为什么CSDN这么辣鸡?我戳进来又要我再发表一次?然后时间就不对了。这个题我之前就写了。

【题目】
LOJ
给定一棵 n n 个点带边权的树,你可以(且必须)删去其中 K K 条边,再连上 K K 条边权为 0 0 的边,价值是大路径权。求所有方案中你能得到的最大价值。
n , K 3 × 1 0 5 n,K\leq 3\times 10^5

【解题思路】
考虑已经删掉了边,答案在树中应该是怎么样的?显然是由原树中恰好 K + 1 K+1 条点不相交的链组成的(单独一个点也算)。

现在问题就是在树上选择 K K 条点不相交的链使得边权和最大。

不妨设 f i , j , 0 / 1 / 2 f_{i,j,0/1/2} 表示在 i i 这个点,子树中选择了 j j 条链,这个点和子树中多少条链相连,的最大权值和。那么转移就是一个类似背包的东西,复杂度 O ( n k 2 ) O(nk^2)

观察到答案关于 K K 的函数应该是一个单峰的函数,不妨考虑进行带权二分。

具体来说,我们二分一个斜率 s s ,每次新选择一条路径时,需要额外失去 s s 点权值,要求的就是选择的最大权值和是多少。

这个问题我们可以在 O ( n ) O(n) 的时间内用一个简单的 DP \text{DP} 求解,方式和之前的做法一样,只是少了 K K 这一维。

这样做复杂度是 O ( n log V ) O(n\log V) 的。

DP \text{DP} 的时候我们需要记录最优的时候至少选择几条链,但由于答案函数可能在一段区间中都取到最优值,无法恰好二分出 K K 条链,不过没有关系,我们一定能构造出来答案,那么需要保留的就是最接近 K K 且不超过 K K 的答案。

一些细节:二分的时候只需要在整数范围内二分就可以了,选择单独一个根节点时,看作与子树中 1 1 条链相连,更新时倒序更新。

一个警告:不要使用pair类型重载大于小于号,否则很有可能出现问题。(下面的代码懒得改了,虽然这样写是对的)

【参考代码】

#include<bits/stdc++.h>
#define fi first
#define se second
#define mkp make_pair
using namespace std;

typedef long long ll;
typedef pair<int,ll>pii;//times,val
const ll inf=1e15;
const int N=3e5+10;
int n,K,tot,head[N];
ll mid,ans,rem;
pii f[N][3];

int read()
{
	int ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

struct Tway{int v,nex,w;}e[N<<1];
void add(int u,int v,int w)
{
	e[++tot]=(Tway){v,head[u],w};head[u]=tot;
	e[++tot]=(Tway){u,head[v],w};head[v]=tot;
}

bool operator <(const pii&a,const pii&b){return a.se==b.se?a.fi>b.fi:a.se<b.se;}
pii Max(const pii&a,const pii&b){return a<b?b:a;}
pii operator +(const pii&a,const pii&b){return mkp(a.fi+b.fi,a.se+b.se);}
pii operator +(const pii&a,const ll&b){return mkp(a.fi,a.se+b);}
void gmax(pii&a,pii b,int c){b.fi+=c;a=Max(a,b);}

void dfs(int x,int fa)
{
	f[x][0]=mkp(0,0);f[x][1]=mkp(1,-mid);f[x][2]=mkp(0,-inf);
	for(int i=head[x];i;i=e[i].nex)
	{
		int v=e[i].v,w=e[i].w;
		if(v==fa) continue; dfs(v,x);
		pii a=Max(f[v][0],Max(f[v][1],f[v][2]));
		//printf("%d %lld\n",a.fi,a.se);
		gmax(f[x][2],f[x][2]+a,0);gmax(f[x][2],f[x][1]+f[v][1]+mid+w,-1);
		gmax(f[x][1],f[x][1]+a,0);gmax(f[x][1],f[x][0]+f[v][1]+w,0);
		gmax(f[x][0],f[x][0]+a,0);
	}
}

int solve()
{
	dfs(1,0);pii res=mkp(0,-inf);
	for(int i=1;i<=n;++i) res=Max(res,Max(f[i][0],Max(f[i][1],f[i][2])));
	if(res.fi<=K) ans=res.se;
	//printf("%lld %d %lld\n",mid,res.fi,res.se);
	return res.fi;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("LOJ2478.in","r",stdin);
	freopen("LOJ2478.out","w",stdout);
#endif
	n=read();K=read()+1;
	for(int i=1,x,y;i<n;++i) x=read(),y=read(),add(x,y,read()); 
	ll l=-1e12,r=1e12;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(solve()<=K) rem=mid,r=mid-1;
		else l=mid+1;
	}
	printf("%lld\n",ans+rem*K);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/87886162