Fake Plastic Trees(树形dp/贪心)

Fake Plastic Trees

传送门 Problem - 1693B - Codeforces 1700

思路

树形dp,贪心。

首先叶子节点一定只要填一次就能填满。为了不浪费,就填叶子结点的最大值。然后以同样的值去填其父节点。父节点获得其子节点的ri值之和,有两种可能,一种是比le小,那么就像叶子节点一样直接填满;否则就忽略,传给其父节点当前的f值。

这是一种贪心思想。

正确性说明(不是证明,不会严谨证明):如果不选最大的填充,那么会亏,就比如一个叶子节点有必要填两次吗?还有就是f值比le大就不用再填了,这个节点已满足,没必要再填了,重点在其父节点及其以上节点上了。换句话说,就算要填还不如等到父节点不够的时候再填,填父节点的性价比比填当前点性价比高。

#include <bits/stdc++.h>
typedef long double ld;
typedef long long ll;
#define pb push_back
#define mk make_pair
#define mt make_tuple
#define eb emplace_back
#define pob pop_back
#define rz resize
#define mem(a,b) memset(a,b,sizeof(a))
#define all(a) (a).begin(),(a).end()
#define rall(a) (a).rbegin(),(a).rend()
#define debug(a) cout<<#a<<"="<<a<<endl;
#define qwe(i,a,b) for(int i=a;i<=b;i++)
#define ewq(i,a,b) for(int i=a;i>=b;i--)
inline ll rr(){ll f=1,x=0;char ch;do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');return f*x;}
using namespace std;
const ll INF=0x3f3f3f3f,inf=0x3f3f3f3f3f3f3f;
const ll mod[2]={int(1e9 + 7), 10007};
const int base[2]={29,31};
const int maxn=2e5+6;
ll qpow(ll x,ll n){ll ans=1;while(n>0){if(n&1)ans=ans*x%mod[1];x=x*x%mod[1];n>>=1;}return ans;}

int n,k;
ll le[maxn],ri[maxn],f[maxn];
ll head[maxn],nxt[maxn],cnt,to[maxn],out[maxn];
void add_edge(int u,int v) {
    nxt[++cnt]=head[u];
    head[u]=cnt;
    to[cnt]=v;
    out[u]++;
}
void init(/* arguments */) {
    cnt=k=0;
    n=rr();
    for(int i=0;i<=n;i++) head[i]=out[i]=f[i]=0;

    qwe(i,2,n) {
        int u=rr();
        add_edge(u,i);
    }
    qwe(i,1,n) le[i]=rr(),ri[i]=rr();

}
void dfs_max(int u,int fa) {
    if(!out[u]) {
        f[fa]+=ri[u]; // 每个点的父节点的最大可能值
        k++;
        return ;
    }
    for(int i=head[u];i;i=nxt[i]) {
        int v=to[i];
        dfs_max(v,u);
    }
    ll va=min(f[u],ri[u]); 
    if(va<le[u]) {
        k++;
        f[fa]+=ri[u];
    }else {
        f[fa]+=va;
    }
}
int main(int argc, char const *argv[]) {
    int t=rr();
    while (t--) {
        init();

        out[0]=1;
        dfs_max(1,0);
    
        std::cout << k << '\n';
    }
    return 0;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值