ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[Codeforces1175G]Yet Another Partiton Problem DP

2020-11-04 23:34:41  阅读:206  来源: 互联网

标签:return Another int mid tp func Codeforces1175G inline Problem


CF1175G

题意:长度为 \(n\) 的序列\(\{a_n\}\),划分成\(k\)段,一段的权值为\(len\times\max{a_i}\),求最小划分权值。\(1\le n, a_i\le2\times10^4, k\le100\)。

很容易写出DP方程:

\[\begin{align*} dp[i][j]&=dp[k][j-1]+(i-k)\times\max\{a_{k+1}\dots a_i\}\\ &=(dp[k][j-1]-k\times max)+i\times max \end{align*} \]

当 \(i\) 固定时,\(max\) 递减且可以分成几段,下面处理这些段。

为处理方便,先忽略掉第二维。

第 \(k\) 段的位置 \(i\), \(L_{k, i}(x)=-ix+dp[i]\)

每次求 \(\min L_{k, i}(max_k)\),注意到 \(-i\) 单调减,\(max_k\) 单调增,可以用双端队列维护。

第 \(k\) 段整体, \(L'_k(x)=max_k\cdot x+\min L_{k, i}(max_k)\)

每次求 \(\min L'_k(i)\),注意到 \(max_k\) 单调减,\(i\) 单调增,但是由于需要回退到历史版本,简单地像上一种情况一样维护会出现副作用,因此直接使用可持久化李超树维护。

迭代 \(i\) 时,会合并一些段,涉及到合并凸包的操作,需要启发式合并。

最终复杂度 \(O(nk\log n)\)。

#include <bits/stdc++.h>
#ifdef LOCAL
#define dbg(args...) std::cerr << "\033[32;1m" << #args << " -> ", err(args)
#else
#define dbg(...)
#endif
inline void err() { std::cerr << "\033[0m\n"; }
template<class T, class... U>
inline void err(const T &x, const U &... a) { std::cerr << x << ' '; err(a...); }
template <class T>
inline void readInt(T &w) {
  char c, p = 0;
  while (!isdigit(c = getchar())) p = c == '-';
  for (w = c & 15; isdigit(c = getchar());) w = w * 10 + (c & 15);
  if (p) w = -w;
}
template <class T, class... U>
inline void readInt(T &w, U &... a) { readInt(w), readInt(a...); }
template <class T, class U>
inline bool smin(T &x, const U &y) { return y < x ? x = y, 1 : 0; }
template <class T, class U>
inline bool smax(T &x, const U &y) { return x < y ? x = y, 1 : 0; }

typedef long long LL;
typedef std::pair<int, int> PII;

constexpr int N(2e4 + 5);
int n, k, a[N], dp[N];
struct Line {
  int k, b;
  inline int func(int x) { return k * x + b; }
  inline bool check(const Line &p, const Line &q) const {
    assert((k < p.k) && (k < q.k));
    return 1LL * (b - p.b) * (q.k - k) <= 1LL * (b - q.b) * (p.k - k);
  }
};
struct Node {
  Node *ls, *rs;
  Line v;
} t[N * 30], *ptr;
void ins(Node *&o, int l, int r, Line x) {
  if (!o) {
    o = ptr++;
    o->ls = o->rs = 0;
    o->v = x;
    return;
  }
  *ptr = *o, o = ptr++;
  int mid = l + r >> 1;
  bool lf = x.func(l) < o->v.func(l);
  bool ri = x.func(r) < o->v.func(r);
  bool mi = x.func(mid) < o->v.func(mid);
  if (mi) std::swap(o->v, x);
  if (l == r || lf == ri) return;
  lf != mi ? ins(o->ls, l, mid, x) : ins(o->rs, mid + 1, r, x);
}
int ask(Node *o, int l, int r, int x) {
  if (!o) return 1e9;
  int mid = l + r >> 1;
  if (x == mid) return o->v.func(x);
  return std::min(o->v.func(x), x < mid ? ask(o->ls, l, mid, x) : ask(o->rs, mid + 1, r, x));
}
struct Block {
  std::deque<Line> q;
  int idx;
  inline void push_back(const Line &x) {
    while (q.size() >= 2 && !x.check(q[q.size() - 2], q[q.size() - 1])) q.pop_back();
    q.push_back(x);
  }
  inline void push_front(const Line &x) {
    while (q.size() >= 2 && !q[1].check(x, q[0])) q.pop_front();
    q.push_front(x);
  }
  inline LL ask(int x) {
    if (q.empty()) return 1e9;
    while (q.size() >= 2 && q[0].func(x) >= q[1].func(x)) q.pop_front();
    return q[0].func(x);
  }
};
void calc(int k) {
 // static LL f[N];
  static Block s[N], *tp, now;
  static Node *rt[N];
  static int f[N];
  tp = s, ptr = t;
  for (int i = 1; i <= n; i++) {
    now.q.clear(), now.idx = i;
    now.q.push_back({-i + 1, dp[i - 1]});
    while (tp != s && a[i] >= a[tp->idx]) {
      if (tp->q.size() < now.q.size()) {
        while (!tp->q.empty()) {
          now.push_front(tp->q.back());
          tp->q.pop_back();
        }
      } else {
        tp->q.swap(now.q);
        while (!tp->q.empty()) {
          now.push_back(tp->q.front());
          tp->q.pop_front();
        }
      }
      tp--;
    }
    tp++;
    tp->q.swap(now.q), tp->idx = i;
    rt[tp - s] = rt[tp - s - 1];
    ins(rt[tp - s], 1, n, {a[i], tp->ask(a[i])});
    f[i] = ask(rt[tp - s], 1, n, i);
  }
  for (int i = 1; i <= n; i++) dp[i] = f[i];
}
int main() {
  readInt(n, k);
  dp[0] = 1e9;
  for (int i = 1, max = 0; i <= n; i++) {
    readInt(a[i]);
    smax(max, a[i]);
    dp[i] = 1LL * max * i;
  }
  for (int i = 2; i <= k; i++) calc(i);
  std::cout << dp[n];
  return 0;
}

标签:return,Another,int,mid,tp,func,Codeforces1175G,inline,Problem
来源: https://www.cnblogs.com/HolyK/p/13929388.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有