很容易想到树形 dp。
考虑在有根树内,每个点都有两种状态:
- 不选自己和父亲的边;
- 要选自己和父亲的边。
那么单独对于子树内部而言,就要分两种情况:
- 最多可以向 \(d_i\) 个孩子连边,对应上述第一种情况,我们称之为 \(f_i\);
- 最多可以向 \(d_i-1\) 个孩子连边,对应上述第二种情况,我们称之为 \(dp_i\)。
最基本的状态是不选自己和子树的连边,答案即为 \(\sum\limits_{j\in ison} f_j\)。
然后发现每次连 \((i,j)\) 这条边,答案会加上 \(mx_j=dp_j+w_{(i,j)}-f_j\)。
那么对于 \(f_i\),就可以挑选前 \(d_i\) 大的 \(mx_j\),答案加上所有 \(>0\) 的 \(mx\) 值。\(dp_i\) 同理。
时间复杂度 \(O(n\log n)\)。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e5+5;
int n,k,u[N],v[N],d[N],r[N];
int m,h[N],to[N*2],nxt[N*2];
ll w[N*2],mx[N],f[N],dp[N];
int cmp(ll x,ll y){return x>y;}
void add(int x,int y,ll z){
to[++m]=y;
w[m]=z;
nxt[m]=h[x];
h[x]=m;
}void dfs(int x,int fa){
for(int i=h[x];i;i=nxt[i]){
int y=to[i];
if(y==fa) continue;
dfs(y,x);
dp[x]+=f[y];
}int k=0;
for(int i=h[x];i;i=nxt[i])
if(to[i]!=fa)
mx[++k]=dp[to[i]]+w[i]-f[to[i]];
sort(mx+1,mx+k+1,cmp);
for(int i=1;i<d[x];i++){
if(mx[i]<=0) break;
dp[x]+=mx[i];
}f[x]=dp[x];
if(mx[d[x]]>0) f[x]+=mx[d[x]];
if(!d[x]) dp[x]=-1e9;
for(int i=1;i<=k;i++) mx[i]=0;
}int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>d[i];
for(int i=1;i<n;i++){
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z);
}dfs(1,0);
cout<<f[1];
return 0;
}
标签:nxt,int,题解,ll,dfs,ABC259F,Edges,mx,dp
From: https://www.cnblogs.com/chang-an-22-lyh/p/18111530