P14150 题解
思路
显然是树形 DP。
对一个节点
- 状态
0 :u 点不选且其父节点被选; - 状态
1 :u 点不选且其父节点不选; - 状态
2 :u 点被选。
那么对于当前节点
以下令
状态
由于父节点选,而当且当前节点不选,则需要从
v 中选取k-1 个点。显然,要贪心地选前
k-1 大的数,答案就是S_1+\sum_{i=1}^{s-1}B_i 。
状态
和状态
0 同理,不过在v 中选k 个点,答案就是S_1+\sum_{i=1}^sB_i 。
状态
注意事项
- 注意本题的图是森林。
AC CODE
#include<bits/stdc++.h>
using namespace std;
#define int long long
int read(){int x=0;char f=1,ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int N=1e6+10;
int n,m,k,a[N],dp[N][3];
vector<int>e[N];
bool vis[N];
void dfs(int u,int fa){
vis[u]=true;
vector<int>vc;
int sum0=0,sum1=0;
for(auto v:e[u])
if(v!=fa){
dfs(v,u);
sum0+=dp[v][0];
sum1+=dp[v][1];
vc.push_back(dp[v][2]-dp[v][1]);
}
sort(vc.begin(),vc.end(),greater<>());
dp[u][0]=sum1;
int cnt0=min(k-1,(int)vc.size());
for(int i=0;i<cnt0;++i)
if(vc[i]>0)
dp[u][0]+=vc[i];
else break;
dp[u][1]=sum1;
int cnt1=min(k,(int)vc.size());
for(int i=0;i<cnt1;++i)
if(vc[i]>0)
dp[u][1]+=vc[i];
else break;
dp[u][2]=a[u]+sum0;
return;
}
signed main(){
n=read(),m=read(),k=read();
for(int i=1;i<=n;++i)
a[i]=read();
while(m--){
int u=read(),v=read();
e[u].push_back(v);
e[v].push_back(u);
}
int ans=0;
for(int i=1;i<=n;++i)
if(!vis[i]){
dfs(i,0);
ans+=max(dp[i][1],dp[i][2]);
}
printf("%lld\n",ans);
return 0;
}