[Codeforces1249F]Maximum Weight Subset

题意

求一棵树的最大点权和子树,且子树里任意两点的距离大于 K K K


题解

树上 D P DP DP(默认 1 1 1为树根)

为了方便把将题目要求的大于 K K K改成大于等于 K + 1 K+1 K+1,即一开始 K = K + 1 K=K+1 K=K+1

算法一

f [ u ] [ k ] f[u][k] f[u][k]表示以 u u u为根的子树里离 u u u最近的点距离 u u u k k k的答案

那么对于 u u u的子节点 v v v

f [ u ] [ k ] = { max ⁡ { f [ v 0 ] [ k − 1 ] + ∑ v ≠ v 0 max ⁡ j ≥ k − 1 f [ v ] [ j ] } k ≥ 1 a [ u ] k = 0 f[u][k]= \begin{cases} \max\{f[v_0][k-1]+\sum_{v\neq v_0}\max_{j\ge k-1}f[v][j]\}&k\ge1\\ a[u]&k=0 \end{cases} f[u][k]={max{f[v0][k1]+v=v0maxjk1f[v][j]}a[u]k1k=0

可以发现这个算法复杂度是 O ( n 4 ) O(n^4) O(n4),而且答案也不好统计

算法二

f [ u ] [ k ] f[u][k] f[u][k]表示以 u u u为根的子树里离 u u u最近的点与 u u u的距离大于等于 k k k的答案

显然 f [ u ] [ k ] = max ⁡ j ≥ k f [ u ] [ j ] ; f [ u ] [ 0 ] f[u][k]=\max_{j\ge k} f[u][j];f[u][0] f[u][k]=maxjkf[u][j];f[u][0]初值为 a [ u ] a[u] a[u]

考虑将 u u u的子节点 v v v的答案合并到 u u u的答案上去

f [ u ] [ k ] = max ⁡ 0 ≤ k ≤ n { f [ u ] [ k ] + f [ v ] [ max ⁡ ( k , K − k ) − 1 ] f [ u ] [ max ⁡ ( k , K − k ) ] + f [ v ] [ k − 1 ] f[u][k]=\max_{0\le k\le n}\begin{cases} f[u][k]+f[v][\max(k,K-k)-1]\\ f[u][\max(k,K-k)]+f[v][k-1] \end{cases} f[u][k]=0knmax{f[u][k]+f[v][max(k,Kk)1]f[u][max(k,Kk)]+f[v][k1]

实现时用一个临时数组将答案存下,枚举完后再更新 f [ u ] [ k ] f[u][k] f[u][k],最后再求一遍后缀最大值

最后答案即为 f [ 1 ] [ 0 ] f[1][0] f[1][0]

时间复杂度为 O ( n 3 ) O(n^3) O(n3)

算法三

考虑对算法二进行优化,设 L e n u Len_u Lenu表示 u u u子树里的点离 u u u最远的距离

那么上面 k k k的枚举范围就可以缩小到 0 ≤ k ≤ L e n v 0\le k\le Len_v 0kLenv,因为 k k k大了之后 f [ v ] f[v] f[v]的值就不存在了

时间复杂度 O ( ∑ O(\sum O(长链长 ) = O ( n 2 ) )=O(n^2) )=O(n2)

算法四

为了方便将 f [ v ] f[v] f[v]往后整体挪一位,根据定义新的 f [ v ] [ 0 ] = f [ v ] [ 1 ] f[v][0]=f[v][1] f[v][0]=f[v][1],那么

f [ u ] [ k ] = max ⁡ { f [ u ] [ k ] + f [ v ] [ max ⁡ ( k , K − k ) ] f [ u ] [ max ⁡ ( k , K − k ) ] + f [ v ] [ k ] f[u][k]=\max\begin{cases} f[u][k]+f[v][\max(k,K-k)]\\ f[u][\max(k,K-k)]+f[v][k] \end{cases} f[u][k]=max{f[u][k]+f[v][max(k,Kk)]f[u][max(k,Kk)]+f[v][k]

可以发现这样 f [ u ] f[u] f[u] f [ v ] f[v] f[v]就是对偶的了

然后可以注意到, D P DP DP只会影响到 0 ≤ k ≤ min ⁡ ( L e n u , L e n v ) 0\le k\le \min(Len_u,Len_v) 0kmin(Lenu,Lenv)(因为长了就一定会有一个没有值了)

于是就可以考虑长链剖分,每次将短链合并到长链上

时间复杂度 O ( n ) O(n) O(n)

void Merge(deque<int>&f,deque<int>&g){//启发式合并
	if(f.size()<g.size())
		swap(f,g);
	vector<int>Tmp=vector<int>(g.begin(),g.end());//复制短链
	for(int k=0;k<(int)g.size();++k){
		int Res=max(k,K-k);
		if(Res<(int)f.size())
			Tmp[k]=max(Tmp[k],f[Res]+g[k]);
		if(Res<(int)g.size())
			Tmp[k]=max(Tmp[k],f[k]+g[Res]);
	}
	int Max=0;
	for(int k=g.size()-1;k>=0;--k){
		Max=max(Max,Tmp[k]);
		f[k]=max(f[k],Max);
	}//更新后缀最大值
}
deque<int> dsu(int u,int p){
	deque<int>Now={a[u]};
	for(auto v:G[u])if(v!=p){
		deque<int>Son=dfs(v,u);
		Son.push_front(Son.front());//整体向后移一位
		Merge(Now,Son);
	}
	return Now;
}

算法五

考虑选了一个点 u u u就加上他的权值 a u a_u au,那么他周围与之距离小于 K K K的点都不能选

考虑把这些点的权值都减去 a u a_u au,相当于造成不选的影响

如果有的点点权变成了负数,那么这个点一定不会选

如果有的点点权还是正数,那就再加上这个点的权值进行 “ “ 补差 ” ” ,就相当于选了这个点而没有选 u u u

然后就是选点顺序的问题,一种可行的方案就是从叶子节点开始,因为叶子节点没有儿子了,不会有后效性

时间复杂度 O ( n 2 ) O(n^2) O(n2)

void dfs(int u,int p,int dis){
    if(dis>=K)return;
    a[u]-=Val;
    for(auto v:G[u])if(v!=p)
    	dfs(v,u,dis+1);
}
inline void Bfs(){
	int h=1,t=1;
    q[1]=vis[1]=1;
    while(h<=t){
        int u=q[h++];
        for(auto v:G[u])
        	if(!vis[v]){
        		vis[v]=1;
        		q[++t]=v;
        	}
    }
}
inline int sol(){
	int Ans=0;
	Bfs();
    for(int i=n;i>=1;--i){
        int u=q[i];Val=a[u];
        if(Val<=0)continue;
        Ans+=Val;
        dfs(u,0,0);
    }
    return Ans;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值