• 跑的比谁都快 51Nod


    香港记者跑的比谁都快是众所周知的常识。

     
    现在,香港记者站在一颗有  nn 个点的树的根结点上(即1号点),编号为  ii 的点拥有权值  a[i]a[i] ,数据保证每个点的编号都小于它任意孩子结点的别号。
     
    我们假定这棵树的每个叶子结点都在发生一个大新闻,香港记者要用最少的耗时去报道其中的任意一个。
     
    若香港记者目前处于第  ii 号点上,那么它可以移动至以  ii 为根的子树上的任意一点  jj ,耗时 a[i]+(ji)pa[i]+(j−i)p ,p为给定常数。
     
    请问这位香港记者搞哪个大新闻的耗时最短?所耗时间是多少?

    Input第一行两个数n<=100000、p<=10,代表树上点的个数以及题中所提及的常数p。 
    接下来n行,第i行有两个数字aii<10^6、faii <i,分别代表i号点的权值与i号点的父亲节点编号(根节点父亲编号为0)。

    数据保证最短耗时不超过10^18.
    Output每组数据输出一行为最短耗时。Sample Input

    10 2
    833 0
    2076 1
    5759 1
    5671 3
    6642 2
    3712 4
    8737 1
    5139 6
    8800 1
    6638 1

    Sample Output

    849


    先谢谢Jessie Liu大佬的点拨。
    题解:
      这个题目,我们先考虑一条链的情况,设dp[i]表示i这个节点到1节点的最小花费,那么dp[i]=dp[j]+cost[j~i],这个是十分显然的,所以在链上枚举i的每个前驱节点就可以。
      换成一棵树,因为一棵树是由很多条链来组成的,对于节点i,我们可以枚举每个祖先来j来dp,状态转移是一样的,但是具体怎么在树上枚举祖先呢?可以开一个栈记录一下所经过的深度的节点就看了。
      因为是指数函数,可以看出有决策单调性,那么就记一下父节点是从哪里转移的,从那里开始枚举就可以了。

    代码:
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <iostream>
    #define MAXN 100100
    #define ll long long
    using namespace std;
    struct edge{
        int first;
        int next;
        int to;
    }a[MAXN*2];
    ll dp[MAXN],node[MAXN],fa[MAXN],stk[MAXN],last[MAXN],ans=1ll<<60;
    int n,p,num=0;
    
    void addedge(int from,int to){
        a[++num].to=to;
        a[num].next=a[from].first;
        a[from].first=num;
    }
    
    ll pw(ll base,int h){
        ll ans=1;
        while(h){
            if(h&1) ans=ans*base;
            base*=base;h>>=1;
        }
        return ans;
    }
    
    void dfs(int now,int dep){
        stk[dep]=now;
        for(int j=last[fa[now]];j<dep;j++){
            ll cnt=dp[stk[j]]+node[stk[j]]+pw(now-stk[j],p);
            if(dp[now]>cnt) dp[now]=cnt,last[now]=j;
        }
        int sz=0;
        for(int i=a[now].first;i;i=a[i].next){
            int to=a[i].to;
            if(to==fa[now]) continue;
            sz++;
            dfs(to,dep+1);
        }
        if(!sz) ans=min(ans,dp[now]);
    }
    
    int main()
    {
        scanf("%d%d",&n,&p);
        memset(dp,37,sizeof(dp));
        for(int i=1;i<=n;i++){
            scanf("%lld%lld",&node[i],&fa[i]);
            addedge(i,fa[i]),addedge(fa[i],i);
        }
        dp[1]=0;
        dfs(1,0);
        printf("%lld",ans);
        return 0;
    }
  • 相关阅读:
    shell 脚本实现yum安装 LAMP 架构的 wordpress
    redis主从及哨兵和cluster集群
    扫描网段中服务器显示状态
    利用for循环输出九九乘法表
    正则表达式取文件后缀
    利用正则表达式实现yes/no判断
    判断输入的IP地址是否合法
    Shell脚本编程基础之shell脚本条件测试命令
    Shell脚本编程基础之shell脚本逻辑运算
    Shell脚本编程基础之shell脚本算术运算
  • 原文地址:https://www.cnblogs.com/renjianshige/p/7573168.html
Copyright © 2020-2023  润新知