51Nod 1789 跑的比谁都快

树形DP+决策单调性

考虑优化DP。
构一条链的数据,打表,发现决策点单调,因此套决策单调性即可 O(nlogn)

知识漏洞,以前对决策单调性的理解一直存在误区。决策单调性是指对于 i < j,那么一定有i的决策点<=j的决策点,但并不保证决策函数是单峰的。因此不能直接从上一个决策点开始枚举,遇到不优就停下来。正确做法是主动转移,然后二分。详见《1D/1D 动态规划优化初步》里面的第一部分。

在树上的话,记录退栈就可以了。

话说这题好像数据很水,卡不掉错误算法?

#include<cstdio>
#include<queue>
#define N 100005
#define cmin(u,v) ((u)>(v)?(u)=(v):0)
using namespace std;
namespace runzhe2000
{
    typedef long long ll;
    const ll INF = 1000000000000000000ll;
    int n, ecnt, p, a[N], last[N], stacnt, licnt;
    ll ans, f[N], pow[N];
    struct edge{int next, to;}e[N<<1];
    void addedge(int a, int b)
    {
        e[++ecnt] = (edge){last[a], b};
        last[a] = ecnt;
    }
    struct stack
    {
        int u, p, from;
    }sta[N], list[N];
    void dfs(int x)
    {
        if(x == 1) f[x] = 0;
        else
        {
            int l = 1, r = stacnt;
            for(; l < r; )
            {
                int mid = (l+r+1)>>1;
                if(sta[mid].p <= x) l = mid;
                else r = mid - 1;
            }
            int u = sta[l].u;
            f[x] = f[u] + a[u] + pow[x - u];
        }
        if(x == 1) sta[++stacnt] = (stack){x, 2, 0};
        else
        {
            int u, p;
            for(;;)
            {
                u = sta[stacnt].u, p = sta[stacnt].p;
                if(x < p && f[x] + a[x] + pow[p-x] < f[u] + a[u] + pow[p-u]) list[++licnt] = sta[stacnt--], list[licnt].from = x;
                else break;
            }
            int l = max(x+1, p+1), r = n+1; 
            for(; l < r; )
            {
                int mid = (l+r)>>1;
                if(f[x] + a[x] + pow[mid-x] < f[u] + a[u] + pow[mid-u]) r = mid;
                else l = mid + 1;
            }
            if(l <= n) sta[++stacnt] = (stack){x, l, 0};
        }
        bool end = 1; for(int i = last[x]; i; i = e[i].next){dfs(e[i].to);end=0;}
        if(end) cmin(ans, f[x]);
        if(sta[stacnt].u == x) --stacnt;
        for(; licnt && list[licnt].from == x; )sta[++stacnt] = list[licnt--];
    }
    void main()
    {
        scanf("%d%d",&n,&p);
        for(int i = 1, x; i <= n; i++)
        {
            scanf("%d%d",&a[i],&x);
            x ? addedge(x, i),0 : 0;
        }
        for(int i = 1; i <= n; i++)
        {
            ll tmp = 1; bool fuck = 0;
            for(int j = 1; j <= p; j++) (tmp * i) < tmp ? fuck = 1 : tmp *= i;
            pow[i] = fuck ? INF : tmp;
        }
        ans = INF;  dfs(1);
        printf("%lld\n",ans);
    }
}   
int main()
{
    runzhe2000::main();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值