• arc073 F many moves(dp + 线段树)


    设dp[i][y]表示一个点在x[i],另一个点在y时最小要走的步数

    那么有以下转移

    对于y != x[i-1]的状态,可以证明,他们直接加|x[i] - x[i-1]|即可(如果有其他方案,不符合对dp的定义)

    当y == x[i-1]时,它可以由其他所有状态转移过来, dp[i][x[i-1]] = min(dp[i][y] + |y - x[i]|)

    把绝对值拆出来,就是需要维护一个dp[i][y] + y 和dp[i][y] - y,建立两个线段树即可。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    using namespace std;
    typedef long long LL;
    const int maxn = 2e5 + 100;
    LL Plus[maxn*4], Minus[maxn*4], tag[maxn*4];
    inline LL abs(LL x) { return x < 0 ? -x : x; }
    inline void Puttag(int o, LL v){
        Plus[o] += v;
        Minus[o] += v;
        tag[o] += v;
    }
    inline void Pushdown(int o){
        if(!tag[o]) return;
        Puttag(o*2, tag[o]);
        Puttag(o*2+1, tag[o]);
        tag[o] = 0;
    }
    inline void Maintain(int o){
        Plus[o] = min(Plus[o*2], Plus[o*2+1]);
        Minus[o] = min(Minus[o*2], Minus[o*2+1]);
    }
    inline bool Cut(int x) { return false; }
    inline bool Check(int x) { return true; }
    inline void Change(int o, int l, int r, int L, int R, LL v){
        if(L > r || R < l || Cut(o)) return;
        if(L <= l && r <= R && Check(o)){
            Puttag(o, v); return;
        }
        int mid = (l+r)/2; Pushdown(o);
        Change(o*2, l, mid, L, R, v);
        Change(o*2+1, mid+1, r, L, R, v);
        Maintain(o);
    }
    inline long long Query(int o, int l, int r, int L, int R, int ty){
        if(L > r || R < l || Cut(o)) return 1e18;
        if(L <= l && r <= R){
            return ty ? Plus[o] : Minus[o];
        }
        int mid = (l+r)/2; Pushdown(o);
        ans = min(Query(o*2+1, mid+1, r, L, R, ty), Query(o*2, l, mid, L, R, ty));
        Maintain(o);
        return ans;
    }
    inline void Insert(int o, int l, int r, int k, LL v, int ty){
        if(l == r) {
            if(ty) Plus[o] = v; else Minus[o] = v;
            return;
        }
        int mid = (l+r)/2; Pushdown(o);
        if(k <= mid) Insert(o*2, l, mid, k, v, ty);
        else Insert(o*2+1, mid+1, r, k, v, ty);
        Maintain(o);
    }
    
    int N, Q, A, B;
    int x[maxn];
    int main(){
        scanf("%d %d %d %d", &N, &Q, &A, &B);
        for(int i = 1; i <= Q; i++) scanf("%d", &x[i]);
        x[0] = B;
        // dp[i][x] = dp[i-1][x] + ||
        // dp[i][x[i-1]] = all(dp[i-1][x]+|x-x[i]|)
        // x <= x[i] -> dp[i-1][x] + x[i] - x
        // x > x[i] -> dp[i-1][x] + x - x[i]
        memset(Plus, 1, sizeof(Plus));
        memset(Minus, 1, sizeof(Minus));
        Insert(1, 1, N, A, A, 1);
        Insert(1, 1, N, A, -A, 0);
        for(int i = 1; i <= Q; i++){
            LL ans = min(Query(1, 1, N, 1, x[i], 0) + x[i], Query(1, 1, N, x[i]+1, N, 1) - x[i]);
            Change(1, 1, N, 1, x[i-1]-1, abs(x[i] - x[i-1]));
            Change(1, 1, N, x[i-1]+1, N, abs(x[i] - x[i-1]));
    
            Insert(1, 1, N, x[i-1], ans + x[i-1], 1);
            Insert(1, 1, N, x[i-1], ans - x[i-1], 0);
        }
        LL ans = 1e18;
        for(int i = 1; i <= N; i++){
            ans = min(ans, Query(1, 1, N, i, i, 0) + i);
        }
        cout<<ans<<endl;
    }
  • 相关阅读:
    Visual C++ 2005如何引用静态链接库(.lib)
    CodeSnippets: Recursively remove all .svn directories [shell] [svn] [bash]
    静态连接库的生成和使用
    vc生成静态库例子
    Remove the .pyc files from current directory tree and from svn (Python recipe) by Senthil Kumaran
    boost 1.52在windows下的配置
    CMake Cross Platform Make
    std::equal_range
    Windows环境下使用Boost
    Js$.extend方法使方法参数更灵活
  • 原文地址:https://www.cnblogs.com/Saurus/p/6816770.html
Copyright © 2020-2023  润新知