Crystalfly 简单树形DP

20 篇文章 0 订阅
题意

一颗树,在边上移动的时间花费为1。每个点有两个权 a a a t t t a a a是一个只能被获取一次的点权, t t t代表当第一次进入这个点的相邻点后多久,这个点的第一个权 a a a会消失。 t t t的取值范围为 1 1 1 3 3 3.现在你从 1 1 1号点开始,任意游走,请问最大能获得的 a a a是多少。

思路

需要特殊关注的是 t t t的范围,一到三。假如我们任意跑到了一个点,那么他的子节点会立即开始计时。假如有若干个子树。我有如下两种抉择:走到其中一棵子树 v v v,之后一路获取 v v v子树的权,最后再去别的子树。第二种是我先前往 v v v,之后不去管 v v v的子节点,返回根,再去另一个子树 w w w。这样操作的话, w w w t t t只能为 3 3 3才能有收益, 1 1 1或者 2 2 2都会导致 w w w的权在触及时已经消失了。是不是还有别的反复横跳的操作呢?不会的,因为最多只能获得两个子节点的 a a a,多次操作反而会使得子树中更多点消失。

我们分别考虑这两种操作。为方便描述,设置如下记号:设 a x a_x ax为上文 x x x的第一个权, t x t_x tx为第二个权。 f x f_x fx x x x的第一权为 0 0 0后,初次到达 x x x后能其子树能获得的最大收益。 g x g_x gx设为x所有子节点 y i y_i yi f y i f_{y_i} fyi之和。 h x h_x hx a x − f x + g x a_x - f_x + g_x axfx+gx。显然最终输出的答案应为 a 1 + f 1 a_1+f_1 a1+f1

对于第一种操作,我们走到了 x x x v v v子树,并一路处理下去,那么我们相当于在 x x x的子节点中,只有 v v v子树能获得完整的收益 a v + f v a_v+f_v av+fv,别的子节点 w i w_i wi都只能获得 f w i f_{w_i} fwi的收益。那么第一种操作的转移就是
f x = max ⁡ y { a y } + g x f_x=\max_{y}\{a_y\}+g_x fx=ymax{ay}+gx
对于第二种操作,我们先进入 v v v子树,再回到 w w w子树并操作,那么对于 w w w子树,可以获得完满的收益 a w + f w a_ w+f_w aw+fw,那对于 v v v子树,首先肯定可以获得一个 a v a_v av,之后我们能获得 f v f_v fv吗?不可以的,因为当到达 v v v时子节点就开始计时了,离开再返回它的子节点们的权值早已消散,那么其实他的收获应该是 g v g_v gv。所以最终的转移也就是
f x = max ⁡ u , v    u ! = v    a n d    t v = 3 { g u + f v + a u + a v + ∑ k    k ! = u    a n d    k ! = v f k } = g x + max ⁡ u , v    u ! = v    a n d    t v = 3 { a u + a v − f u + g u } = g x + max ⁡ u , v    u ! = v    a n d    t v = 3 { h u + a v } f_x=\max_{u,v\;u!=v\; and \; t_v=3}\{g_u+f_v+a_u+a_v+\sum_{k\;k!=u\;and\;k!=v}f_k\} \\=g_x+\max_{u,v\;u!=v\; and \; t_v=3}\{a_u+a_v-f_u+g_u\} \\=g_x+\max_{u,v\;u!=v\; and \; t_v=3}\{h_u+a_v\} fx=u,vu!=vandtv=3max{gu+fv+au+av+kk!=uandk!=vfk}=gx+u,vu!=vandtv=3max{au+avfu+gu}=gx+u,vu!=vandtv=3max{hu+av}
对于最后一个 max ⁡ \max max的维护,维护子节点的 h h h最大值和次大值就好了,复杂度是同阶的,引入set等乱搞也是可以的,亲测没有慢多少

复杂度
O ( n + m ) O(n+m) O(n+m)

教训/收获
代码
#include<cstdio>
#include<iostream>
#include<set>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#include<bitset>
#include<functional>
#include<complex>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define de(i) cout<<#i<<": "<<i<<endl;
#define int long long
//#define double long double
using namespace std;
    typedef long long ll;
    const int maxn=1000005;
    const int inf=0x3f3f3f3f3f3f3f3f;
    int n,m,k;
    int a[maxn],t[maxn];
    int f[maxn],g[maxn],h[maxn];
    vector<int>e[maxn];
    void dp(int x,int fa){
        int maxx=-inf;
        pair<int,int>max1H={-inf,-inf},max2H={-inf,-inf};
        int son=0;
        for(auto y:e[x]){
            if(y==fa)continue;
            dp(y,x);
            son++;
            g[x]+=f[y];
            maxx=max(maxx,a[y]);
            if(h[y]>=max1H.first){
                max2H=max1H;
                max1H={h[y],y};
            }
            else if(h[y]>=max2H.first)
                max2H={h[y],y};
        }
        if(son>1){
            for(auto y:e[x]){
                if(y==fa)continue;
                if(t[y]==3){
                    if(max1H.second==y){
                        maxx=max(maxx,a[y]+max2H.first);
                    }
                    else{
                        maxx=max(maxx,a[y]+max1H.first);
                    }
                }
            }
        }
        f[x]=max(f[x],g[x]+maxx);
        h[x]=a[x]-f[x]+g[x]; 
    }
    void init(){
        for(int i=1;i<=n;i++){
            f[i]=0;g[i]=0;h[i]=0;e[i].clear();
        }
    }
    void solve(){
        cin>>n;
        init();
        for(int i=1;i<=n;i++)   cin>>a[i];
        for(int i=1;i<=n;i++)   cin>>t[i];
        for(int i=1;i<n;i++){
            int u,v;
            cin>>u>>v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        dp(1,-1);
        cout<<f[1]+a[1]<<endl;
    }

    signed main(){
        IOS
        #ifdef THESUNSPOT
            auto st=clock();
            freopen("IO\\in.txt","r",stdin);
            freopen("IO\\out.txt","w",stdout);
        #endif
        int tn=1;
        cin>>tn;
        while(tn--){
            solve();
        }
        #ifdef THESUNSPOT
            auto ed=clock();
            cout<<endl<<"time: "<<ed-st<<"ms."<<endl;
            srand(time(0));
            cout<<"version: "<<rand()%1000<<endl;
        #endif
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值