CF1249F Maximum Weight Subset 题解 / 长链剖分复习
CF1249F Maximum Weight Subset 题解
题目大意#
给定一个 个节点的树,每个节点有点权 。从中选出若干节点,要求这些点两两之间距离大于给定常数 ,使得点权和最大。
Solve#
给出一种线性做法。前置知识:长链剖分优化 DP。
考虑一个 DP:设 表示在 的子树里选点,选出的点距离 号点的最短距离为 ,这种情况下的最大点权和。暴力转移是简单的:
总复杂度为 。
第二维和深度(距离)有关,容易长链剖分优化到 ,即 号节点直接继承其重儿子的 值,对于轻儿子,枚举上式种的 转移。复杂度约为 。这部分的代码如下,用指针实现:
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]);
}
}
}
瓶颈在于对 所在链长的枚举。所以我们考虑对 讨论,因为它的最大值为 所在链长,均摊是 级别的。
- 若 ,,此时 ,可以暴力枚举 ,那么可以对这个 贡献的 需要满足 ,这是一段后缀,所以我们可以维护 为 的后缀最大值,就有转移:
- 若 ,,由于本身就有 ,此时我们可以枚举 ,那么可以对这个 贡献的 需要满足 ,有转移:
至此,总复杂度为 。
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】