首页 > 其他分享 >CF1249F Maximum Weight Subset 题解 / 长链剖分复习

CF1249F Maximum Weight Subset 题解 / 长链剖分复习

时间:2024-10-28 15:21:46浏览次数:6  
标签:CF1249F Subset get int 题解 leq son fa dp

CF1249F Maximum Weight Subset 题解

题目大意

给定一个 \(n\) 个节点的树,每个节点有点权 \(a_i\)。从中选出若干节点,要求这些点两两之间距离大于给定常数 \(k\),使得点权和最大。

Solve

给出一种线性做法。前置知识:长链剖分优化 DP。

考虑一个 DP:设 \(f(u,d)\) 表示在 \(u\) 的子树里选点,选出的点距离 \(u\) 号点的最短距离为 \(d\),这种情况下的最大点权和。暴力转移是简单的:

\[f(u,\min(j,i+1))\longleftarrow f(u,j)+f(v,i) \]

总复杂度为 \(O(n^3)\)。

第二维和深度(距离)有关,容易长链剖分优化到 \(O(n^2)\),即 \(u\) 号节点直接继承其重儿子的 \(f\) 值,对于轻儿子,枚举上式种的 \(i,j\) 转移。复杂度约为 \(O(n^2)\)。这部分的代码如下,用指针实现:

void dp(int u,int fa)
{
	if(son[u])	dp(son[u],u);//先遍历重儿子
	f[u][0]=a[u];
	for(int i=k+1;i<h[u];i=-~i)
		f[u][0]=max(f[u][0],f[u][i]+a[u]);
	for(int v:e[u])
		if(v!=fa&&v!=son[u])
		{
			dp(v,u);
			for(int i=0;i<h[u];i=-~i)	now[i]=f[u][i];//用上个版本的
			for(int i=0;i<h[v];i=-~i)//h[v] 为 v 节点的链长
			{
				f[u][i+1]=max(f[u][i+1],f[v][i]);
				for(int j=max(k-i,0ll);j<h[u];j=-~j)
					f[u][min(i+1,j)]=max(f[u][min(i+1,j)],now[j]+f[v][i]);
			}
		}
}

瓶颈在于对 \(u\) 所在链长的枚举。所以我们考虑对 \(\min(i+1,j)\) 讨论,因为它的最大值为 \(v\) 所在链长,均摊是 \(n\) 级别的。

  • 若 \(j\leq i\),\(\min(i+1,j)=j\),此时 \(1\leq j\leq h(v)\),可以暴力枚举 \(j\),那么可以对这个 \(j\) 贡献的 \(i\) 需要满足 \(j\leq i<h(v),i+j+1>k\iff i\geq k-j\),这是一段后缀,所以我们可以维护 \(g(v)\) 为 \(f(v)\) 的后缀最大值,就有转移:

\[f(u,j)\longleftarrow f(u,j)+g(v,\max(j,k-j)) \]

  • 若 \(j>i\),\(\min(i+1,j)=i+1\),由于本身就有 \(0\leq i<h(v)\),此时我们可以枚举 \(i\),那么可以对这个 \(i\) 贡献的 \(j\) 需要满足 \(i<j<h(u),i+j+1>k\iff j\geq k-i\),有转移:

\[f(u,i+1)\longleftarrow g(u,\max(i+1,k-i))+f(v,i) \]

至此,总复杂度为 \(O(\sum链长)=O(n)\)。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
	short f=1;
	int x=0;
	char c=getchar();
	while(c<'0'||c>'9')	{if(c=='-')	f=-1;c=getchar();}
	while(c>='0'&&c<='9')	x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
const int N=2e5+10;
int n,k,a[N];
vector<int>e[N];
int mem[2][N]/*内存池*/,*f[N],*g[N],h[N],son[N],tim;
void get_son(int u,int fa)
{
	h[u]=1;
	for(int i:e[u])
		if(i!=fa)
			get_son(i,u),h[u]=max(h[u],h[i]+1);
	if(h[u]>h[son[fa]])	son[fa]=u;
}
void get_chain(int u,int fa)//分配内存
{
	tim=-~tim;
	f[u]=mem[0]+tim;g[u]=mem[1]+tim;
	if(son[u])	get_chain(son[u],u);
	for(int i:e[u])
		if(i!=fa&&i!=son[u])	get_chain(i,u);
}
void dp(int u,int fa)
{
	if(son[u])	dp(son[u],u);
	f[u][0]=a[u]+(k+1<h[u]?g[u][k+1]:0);
	g[u][0]=max(f[u][0],h[u]>1?g[u][1]:0);
	for(int v:e[u])
		if(v!=fa&&v!=son[u])
		{
			dp(v,u);
			for(int i=0;i<h[v];i=-~i)
				if(max(i,k-i)<h[v])
					f[u][i]=max(f[u][i],f[u][i]+g[v][max(i,k-i)]);
			for(int i=0;i<h[v];i=-~i)
			{
				f[u][i+1]=max(f[u][i+1],f[v][i]);
				if(max(i+1,k-i)<h[u])
					f[u][i+1]=max(f[u][i+1],f[v][i]+g[u][max(i+1,k-i)]);
			}
			for(int i=h[v];i>=0;i=~-i)
				g[u][i]=max(i+1<h[u]?g[u][i+1]:0,f[u][i]);
		}
}
signed main()
{
	n=read();k=read();
	for(int i=1;i<=n;i=-~i)	a[i]=read();
	for(int i=1,u,v;i<n;i=-~i)
		u=read(),v=read(),
		e[u].push_back(v),e[v].push_back(u);
	get_son(1,0);get_chain(1,0);dp(1,0);
	return printf("%lld",*max_element(f[1],f[1]+h[1])),0;
}

标签:CF1249F,Subset,get,int,题解,leq,son,fa,dp
From: https://www.cnblogs.com/sorato/p/18508473

相关文章

  • Codeforces Round 982 (Div. 2) 10.26 (ABC)题解
    CodeforcesRound982(Div.2)10.26(ABC)题解A.RectangleArrangement数学(math)题意:有一个无限长宽的方形网格,初始为白色,现在有\(n\)个印章,每个印章有自己的宽\(w_i\)和高\(h_i\)。印章会使得网格涂色,变成黑色。这\(n\)个印章都需要使用一次,需要求解出最后网格中黑色......
  • Codeforces Round 982 (Div. 2) 题解(A-D)
    目录A思路codeB思路codeC思路卡题原因codeD思路未ac原因codeCodeforcesRound982(Div.2)A思路因为图形可以重叠,所以答案就是最长的长和最长的宽组成的矩形周长.codevoidfunc(void){ intn; cin>>n; intl=0,r=0; while(n--) { intx,y; cin>>x>>y......
  • 题解:CF1666J Job Lookup
    被迫来写篇题解。首先,第一个要求我们只需要在递归构造的时候保证子树对应区间连续即可,现在考虑第二个要求。就题目中的二叉树而言,想要确定其结构,我们只需要关注这段区间,即这棵子树根节点的编号,又因为子树区间连续,所以我们不难想到区间动态规划。设\(dp_{l,r}\)表示\(l\simr......
  • 【最新华为OD机试E卷-支持在线评测】机器人活动区域(200分)多语言题解-(Python/C/Java
    ......
  • CF1738F 题解
    blog。duel的时候对上了脑电波很快过了,记一下这种我本来完全不会的题。肯定是搞掉平方。把\(n_c\)移到左边:\(\dfrac{\sum\limits_{u\inS}deg_u}{|S|}=\text{平均数}\le|S|\)。然后直接放缩左边,于是一个充分条件是:\[\max\limits_{u\inS}deg_u\le|S|\]考虑构造合法解。......
  • CSP-S 2024 游记/题解
    CSP-S2024去年S打成屎了,我要蓝√!!!!!!!!CSP怎么能不CS?CS了一个上午,顺便背了下快读。进厂!看见了退役的同学在做志愿者,祝他未来可期吧。也希望自己能够超常发挥。垃圾鼠标真难用。很好,全是传统题。只有T2开了两秒。T1,这不排个序然后优先队列乱搞就好了。5min过了,NOIP我来......
  • Removing People 题解
    前言题目链接:Atcoder;洛谷。题意简述\(n\)人站成一个圆圈,按顺时针方向依次为\(1,2,\cdots,n\)。每个人面对的方向由长度为\(n\)的字符串\(S\)给出。对于第\(i\)个人,如果\(S_i=\texttt{L}\),则\(i\)面向逆时针方向。如果\(S_i=\texttt{R}\),则面向顺时针方向。......
  • 字节跳动青训营 X 豆包MarsCode入营考核部分题解
    中等:观光景点组合得分问题小R正在研究一组观光景点,每个景点都有一个评分,保存在数组 values 中,其中 values[i] 表示第 i 个观光景点的评分。同时,景点之间的距离由它们的下标差 j-i 表示。一对景点 (i<j) 的观光组合得分为 values[i]+values[j]+i-j,也就......
  • ZZJC新生训练赛第十场题解
    先给出比赛链接:https://vjudge.net/contest/667035下面说一下难度分层:(同一难度下按字典序排序,数字为cf难度分)800:ABEG1100:D1200:C1400:H1500:F原题链接A:https://codeforces.com/contest/1850/problem/AB:https://codeforces.com/contest/1991/problem/......
  • Codeforces Round 981 (Div. 3) 题解(A-E)
    目录分析A思路代码B思路卡题原因代码C思路卡题原因codeD思路卡题原因代码E思路wa原因codeCodeforcesRound981(Div.3)分析这场整体发挥都不好,虽然题也抽象,但是对于这些题完全没必要卡这么久.正常至少能看到\(\mathbf{F}\)A思路因为边界\(n\)管辖\(\pm\),而Sak......