[ABC259F] Select Edges 题解

很容易想到树形 dp。

考虑在有根树内,每个点都有两种状态:

  1. 不选自己和父亲的边;
  2. 要选自己和父亲的边。

那么单独对于子树内部而言,就要分两种情况:

  1. 最多可以向 \(d_i\) 个孩子连边,对应上述第一种情况,我们称之为 \(f_i\)
  2. 最多可以向 \(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;
}
posted @ 2024-04-02 21:16  长安一片月_22  阅读(3)  评论(0编辑  收藏  举报