[2016湖南长沙培训Day4][前鬼后鬼的守护 chen] (动态开点线段树+中位数 or 动规 or 贪心+堆优化)

题目大意

给定一个长度为n的正整数序列,令修改一个数的代价为修改前后两个数的绝对值之差,求用最小代价将序列转换为不减序列。

其中,n满足小于500000,序列中的正整数小于10^9

题解(引自mzx神犇的题解)

本次test跪0了,尴尬

解法1(40分)

考虑dp

设到第i个数为止,序列中数全部<=j的最小代价为f[i][j]

可以推出f[i][j]=min{f[i-1][j]+|ai-j|,f[i][j-1]}

解法2(60分)

是对于第一个dp思路的优化

既然数字是固定的,可以离散化,降低空间时间复杂度

解法3(100分)

斜率优化dp,用线段树维护斜率

解法4(100分,引自zyz大神的解法)

玄学的单调栈问题

由于区间中的数是单调不减的,所以最后得出的序列一定是一段一段的各个数值相等的序列区间

而要最小化代价,可以取这些区间中的中位数作为标准来修改每个区间,并用单调栈求解最优的区间划分

还需要用动态开点线段树维护区间中位数

orz

#include <stdio.h>
#include <string.h>

const int MAXN = 5e5 + 10;
const int INF = 1e9 + 10;

inline int abs(int x){
  return x < 0 ? -x : x;
}

int Rin(){
  int x = 0, c = getchar(), f = 1;
  for(; c < 48 || c > 57; c = getchar())
    if(!(c ^ 45))
      f = -1;
  for(; c > 47 && c < 58; c = getchar())
    x = (x << 1) + (x << 3) + c - 48;
  return x * f;
}

namespace Arcueid{
  struct Node{
    Node *l, *r;
    int s;
    Node(){}
    Node(Node *_l, Node *_r, int _s) : l(_l), r(_r), s(_s) {}
  }*_nil = new Node(), *nil = (*_nil = Node(_nil, _nil, 0), _nil), pool[MAXN * 32], *top = pool;
  Node *reborn(){
    *top = Node(nil, nil, 0);
    return top++;
  }
  void death(Node *&pr, Node *pl, int l, int r, int v){
    pr = reborn(); *pr = *pl; pr->s++;
    if(l + 1 < r){
      int mid = (l + r) >> 1;
      v < mid ? death(pr->l, pl->l, l, mid, v) : death(pr->r, pl->r, mid, r, v);
    }
  }
  int secret(Node *pr, Node *pl, int l, int r, int k){
    if(l + 1 == r)return l;
    int c = pr->l->s - pl->l->s, mid = (l + r) >> 1;
    return c < k ? secret(pr->r, pl->r, mid, r, k - c) : secret(pr->l, pl->l, l, mid, k);
  }
  int feel(Node *pr, Node *pl){
    int c = pr->s - pl->s;
    return secret(pr, pl, 0, INF, (c + 1) >> 1);
  }
}

Arcueid::Node *rt[MAXN] = {Arcueid::nil};

namespace moon{
  void cause(){
    int n, a[MAXN], stay = 1, night[MAXN], shiki[MAXN];

    //    freopen("chen.in", "r", stdin);
    //    freopen("chen.out", "w", stdout);

    n = Rin();
    for(int i = 1; i <= n; i++){
      a[i] = Rin();
      Arcueid::death(rt[i], rt[i-1], 0, INF, a[i]);
      for(; stay > 1 && Arcueid::feel(rt[i], rt[night[stay - 1]]) < shiki[stay - 1]; stay--);
      shiki[stay] = Arcueid::feel(rt[i], rt[night[stay-1]]);
      night[stay++] = i;
    }
    long long ans = 0;
    for(int i = 1; i < stay; i++)
      for(int j = night[i - 1] + 1; j <= night[i]; j++)
    ans += abs(a[j] - shiki[i]);

    printf("%lld
", ans);
    //    fclose(stdin);
    //    fclose(stdout);
  }
}

int main(){
  moon::cause();
  return 0;
}

解法5(100分)

其实这个解法更玄

但最简洁的往往是最好的

#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

inline int read(){
  int x = 0, c = getchar(), f = 1;
  for(; c < 48 || c > 57; c=getchar())
    if(!(c ^ 45))
      f = -1;
  for(; c > 47 && c < 58; c=getchar())
    x = (x << 1) + (x << 3) + c - 48;
  return x * f;
}

long long ans = 0;
priority_queue<int> q;

int main(){
  int n = read();
  for(int i = 1; i <= n; i++){
    int x = read();
    q.push(x);
    ans += q.top() - x;
    if(x ^ q.top())
      q.pop();
  }
  printf("%lld
", ans);
  return 0;
}
原文地址:https://www.cnblogs.com/keshuqi/p/6231334.html