zoukankan      html  css  js  c++  java
  • 线段树+dp+贪心 Codeforces Round #353 (Div. 2) E

    http://codeforces.com/contest/675/problem/E

    题目大意:有n个车站,每个车站只能买一张票,这张票能从i+1到a[i]。定义p[i][j]为从i到j所需要买的最小票数。问sigma(p)的和是多少。

    思路:感觉我的dp定义能力还是太弱了啊,我刚开始还定义成dp[i]表示1~i-1到i所需要的最小花费和,后来发现这样子我转移不了啊!!

    于是重新定义dp[i]表示从i出发到i+1~n所需要的最小票数,然后这样定义就能很好的解决问题啦。然后我们每次贪心的选取j = i+1~a[i]中的a[j]跑的最远的那个,然后进行转移就好了,具体的用线段树维护一下区间吧。

    TAT感觉是不难的题目

    //看看会不会爆int!数组会不会少了一维!
    //取物问题一定要小心先手胜利的条件
    #include <bits/stdc++.h>
    using namespace std;
    #define LL long long
    #define ALL(a) a.begin(), a.end()
    #define pb push_back
    #define mk make_pair
    #define fi first
    #define se second
    #define haha printf("haha
    ")
    const int maxn = 1e5 + 5;
    int a[maxn];
    int n;
    struct Tree{
        int lpos, rpos;
        Tree(int l = 0, int r = 0): lpos(l), rpos(r){}
    }tree[maxn << 2];
    
    inline void push_up(int o){
        int lb = o << 1, rb = o << 1 | 1;
        tree[o].rpos = max(tree[lb].rpos, tree[rb].rpos);
        if (tree[lb].rpos >= tree[rb].rpos) {
            tree[o].lpos = tree[lb].lpos;
        }
        else tree[o].lpos = tree[rb].lpos;
    }
    
    void build_tree(int l, int r, int o){
        if (l == r){
            tree[o].rpos = a[l];
            tree[o].lpos = l;
            return ;
        }
        int mid = (l + r) / 2;
        if (l <= mid) build_tree(l, mid, o << 1);
        if (r > mid) build_tree(mid + 1, r, o << 1 | 1);
        push_up(o);
    }
    
    Tree query(int l ,int r, int ql, int qr, int o){
        if (ql <= l && qr >= r){
            return tree[o];
        }
        Tree ans, re;
        ans.lpos = re.lpos = ans.rpos = re.rpos = 0;
        int mid = (l + r) / 2;
        if (ql <= mid)
            ans = query(l, mid, ql, qr, o << 1);
        if (qr > mid)
            re = query(mid + 1, r, ql, qr, o << 1 | 1);
        if (ans.rpos < re.rpos) ans = re;
        return ans;
    }
    
    LL dp[maxn];///定义从i开始走到n所需要的最小花费
    int main(){
        scanf("%d", &n);
        a[n] = n;
        for (int i = 1; i <= n - 1; i++) scanf("%d", a + i);
        memset(tree, 0, sizeof(tree));
        build_tree(1, n, 1);
        LL ans = 0;
        for (int i = n - 1; i > 0; i--){
            Tree pos = query(1, n, i + 1, a[i], 1);
            dp[i] = n - i + dp[pos.lpos] - (a[i] - pos.lpos);
            ans += 1LL * dp[i];
        }
        printf("%lld
    ", ans);
        return 0;
    }
    View Code
  • 相关阅读:
    响应式Web设计- 背景图片
    55、装饰器的写法以及应用场景。
    54、用尽量多的方法实现单例模式
    53、 什么是反射?以及应用场景?
    52、列举面向对象中的特殊成员以及应用场景
    51、静态方法和类方法区别?
    算法----练习题2
    50、如何判断是函数还是方法?
    49、是否使用过functools中的函数?其作用是什么?
    48、面向对象中super的作用?
  • 原文地址:https://www.cnblogs.com/heimao5027/p/5924128.html
Copyright © 2011-2022 走看看