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;
}
原文地址:https://www.cnblogs.com/Saurus/p/6816770.html