AtCoder Beginner Contest 259 F - Select Edges

树形dp

F - Select EdgesAtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.https://atcoder.jp/contests/abc259/tasks/abc259_f思路:我们先寻找状态,如果对于每个父亲节点考虑,会发现记录状态很麻烦,f[u][v][0/1],对于子节点是否进行连接.这样确定状态的话会爆内存,那么我们如果人要记录子节点与父亲节点是否相连这个状态,可以取消从父亲节点考虑,而从儿子节点考虑,f[v][0/1],f[v][0]状态表示为节点v没有一条边向上延伸的最大值,f[v][1]则为从节点v向上延伸一条边的最大值.

处理好了之后,只需要对于父亲节点进行连接即可.状态转移方程式如下:(这个式子只是大概模型)

\large f[u][0]=\sum_{i=1}^{d[i]}f[v][1]+\sum_{i=d[i]+1}^{n}f[v][1]

\large f[u][1]=\sum_{i=1}^{d[i]-1}f[v][1]+\sum_{i=d[i]}^{n}f[v][1]

我们不连接当前父亲节点的所有子节点,再将父亲节点和当前子节点进行链接产生的贡献进行排序,取前d[i]-1条进行链接,其余不连接,传的值还是f[v][0],连接到的点传的就是f[v][1].因为f[u][1]表示要延伸一条边网上,就要至少留一条边给父亲节点,需要注意的是,如果我链接这个之后的贡献小于不连接的贡献也就,没有必要链接了.f[u][0]就直接链接就可以了.

这里一定要搞清楚的是要不要链接,取对于

{f[v][1]+w-f[v][0]}

排序之后的数组,当>0就表示链接的贡献大于不连接的贡献.分别对于父节点的状态取最多前d[i]个和d[i-1]个进行链接就行了.

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> pii;
const int N =3e5+10,mod=998244353;
int d[N],f[N][2];
struct node
{
    int to,ne,val;
}edge[2*N];
int h[N],tot=0;
void add(int x,int y,int w)
{
    edge[++tot].to=y;
    edge[tot].ne=h[x];
    edge[tot].val=w;
    h[x]=tot;
    return ;
}
bool cmp(int a,int b)
{
    return a>b;
}
void dfs(int u,int fa)
{
    int sum=0;
    vector<int>ve;
    for(int i=h[u];i!=-1;i=edge[i].ne)
    {
        int v=edge[i].to;
        if(v==fa)
            continue;
        dfs(v,u);
    }
    for(int i=h[u];i!=-1;i=edge[i].ne)
    {
        int v=edge[i].to;
        int w=edge[i].val;
        if(v==fa)
            continue;
        sum+=f[v][0];
        ve.push_back({f[v][1]+w-f[v][0]});
    }
    sort(ve.begin(),ve.end(),cmp);
    if(d[u]==0)
    {
        f[u][0]=sum;
        f[u][1]=-1e18;
    }
    else
    {
        f[u][0]=f[u][1]=sum;
        for(int i=0;i<ve.size();i++)
        {
            if(ve[i]<0)
                break;
            if(i<d[u])
                f[u][0]+=ve[i];   
            if(i<d[u]-1)
                f[u][1]+=ve[i];
        }
    }
    return ;    
}
void solve()
{
    memset(h,-1,sizeof h);
    int n,x,y,w;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>d[i];
    for(int i=1;i<n;i++)
    {
        cin>>x>>y>>w;
        add(x,y,w);
        add(y,x,w);
    }
    dfs(1,-1);
    cout<<f[1][0]<<"\n";
    return;
}
signed main()
{
    solve();
    return 0;
}

注意细节处理

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值