zoukankan      html  css  js  c++  java
  • 肥宅快乐树 换根+树形DP/dfs

    肥宅快乐树是一棵神秘而巨大的树,它长有许多枝条和节点,每条枝连接树中两个节点,每个节点上都长有一瓶肥宅快乐水。 何老板是肥宅快乐水的资深爱好者。历经艰难,他终于找到了这棵传说中的快乐树。他想要获取树上所有的快乐水,迫不及待地想从树根往树上爬。 每经过一条树枝都会耗费一定体力。而且快乐树自带防御功能,即每条枝上都有一个一次性陷阱,一旦踏上该枝,何老板就会被立即弹射回地面,他得重新从根往上爬。 (注1:一次性陷阱是指,陷阱只在第一次经过该枝时有效) (注2:从i号点回到i的父亲节点,不耗费体力) 每个节点上都有开有一些肥宅快乐花,花的香气可以让何老板经过当前节点所连的树枝时,耗费的体力值减少。不同节点的快乐花数量不一,产生的减少体力消耗的效果也不一定相同。 何老板发现快乐树还有一个神秘“换根”功能。何老板可以将指定节点变换为树根,变换后,树中树枝连接情况保持不变。但是只能进行一次换根操作。 快乐树共n个节点,编号1到n,开始时1号点为根。 何老板想知道,以哪个节点为根才能使他收集齐n瓶快乐水耗费的总体力最少。请你帮你计算。


    发现每条边都经过儿子节点的size值
    换根的话只有一条边的贡献改变
    考虑换根的话 可以dfs 也可以树形DP 以子树为状态
    code:

    #include<stdio.h> 
    #include<bits/stdc++.h> 
    using namespace std; 
    #define maxnn 1000010 
    #define ll long long  
    int n; 
    ll las[maxnn],en[maxnn],le[maxnn],tot,nex[maxnn]; 
    ll size[maxnn],w[maxnn]; 
    int mark[maxnn]; 
    ll ans=0; 
    ll root=1; 
    ll cnt=0; 
    ll aass=0; 
    ll pos=0; 
    
    void add(int a,int b,ll c) 
    { 
        en[++tot]=b; 
        nex[tot]=las[a]; 
        las[a]=tot; 
        le[tot]=c; 
    } 
    void dfs1(int v,int fa) 
    { 
        size[v]=1; 
        for(int i=las[v];i;i=nex[i]) 
        { 
                int u=en[i]; 
                if(u!=fa) 
                { 
                    dfs1(u,v); 
                    size[v]+=size[u];  
                } 
         
        } 
    
    } 
    void dfs2(int v,int fa) 
    { 
        for(int i=las[v];i;i=nex[i]) 
        { 
                int u=en[i]; 
                if(u!=fa) 
                { 
                    ans=ans+le[i]*size[u]-size[u]*w[v]; 
                    dfs2(u,v); 
                } 
        } 
    } 
    void huan(int v,int fa) 
    { 
        for(int i=las[v];i;i=nex[i]) 
        { 
            int u=en[i]; 
            if(u!=fa&&(!mark[u])) 
            {     
                int tmp1=size[v]-size[u]; 
                int tmp2=size[v]; 
                int tmp3=size[u]; 
                mark[u]=1; 
                ans=ans-le[i]*size[u]+size[u]*w[v]+(size[v]-size[u])*(le[i]-w[u]); 
                size[u]=size[u]+tmp1; 
                size[v]=tmp1; 
                if(aass>ans) 
                { 
                    pos=u; 
                    aass=ans; 
                } 
                huan(u,v); 
                ans=ans+le[i]*tmp3-tmp3*w[v]-(tmp2-tmp3)*(le[i]-w[u]); 
                size[v]=tmp2; 
                size[u]=tmp3; 
            } 
        } 
    } 
    int main() 
    { 
        //freopen("happytree.in","r",stdin); 
        //freopen("happytree.out","w",stdout); 
        ll x,y,z; 
        cin>>n; 
        for(int i=1;i<=n;i++) 
        { 
            scanf("%lld",&w[i]); 
        } 
        for(int i=1;i<n;i++) 
        { 
            scanf("%lld%lld%lld",&x,&y,&z); 
            add(x,y,z); 
            add(y,x,z); 
        } 
        dfs1(1,1); 
        pos=1; 
        dfs2(1,1); 
        aass=ans;mark[1]=1; 
        huan(root,root);  
        cout<<pos<<endl; 
        cout<<aass<<endl; 
    }
    
    刀剑映出了战士的心。而我的心,漆黑且残破
  • 相关阅读:
    方法的重载理解
    JAVA 三种循环的总结
    模拟时间倒计时
    制作漂浮广告效果
    js+css+html实现抽奖小程序
    将系统时间转换为汉字表示的四种方法
    简单的导航栏
    模仿光标闪烁,光标移动,自动切换背景
    sublime插件emmet安装和使用
    现在有一张半径为r的圆桌,其中心位于(x,y),现在他想把圆桌的中心移到(x1,y1)。每次移动一步,都必须在圆桌边缘固定一个点然后将圆桌绕这个点旋转。问最少需要移动几步。
  • 原文地址:https://www.cnblogs.com/OIEREDSION/p/11565434.html
Copyright © 2011-2022 走看看