BZOJ5125: [Lydsy1712月赛]小Q的书架【决策单调性优化DP】【BIT】【莫队】【分治】

小Q有n本书,每本书有一个独一无二的编号,现在它们正零乱地在地上排成了一排。

小Q希望把这一排书分成恰好k段,使得每段至少有一本书,然后把每段按照现在的顺序依次放到k层书架的每一层上去。将所有书都放到书架上后,小Q这才突然意识到它们是乱序的,他只好把每一层的书分别按照编号

从小到大排序。排序每次可以在1单位时间内交换同一层上两本相邻的书。

请写一个程序,帮助小Q计算如何划分这k段,且如何交换这些书,使得总交换次数最少。

Input

第一行包含两个正整数n; k(1≤n≤40000;1≤k≤min(10; n))。

第二行包含n个互不相同的正整数a1,a2,..., an(1≤ai≤n),分别表示地面上每本书的编号。

Output

输出一行一个整数,即最少的总交换次数。

Examples

stdin

6 3

4 3 6 2 5 1

stdout

1

Notes

\([4,3,6][2,5][1]\)划分,需要排序1 + 0 + 0 = 1次。


思路

分成k段,最小化逆序对个数之和

非常套路了吧

决策单调性非常显然

那么就对于每一层进行分治

然后中间怎么维护逆序对个数?

可以用莫队+树状数组

因为首尾删/加数的逆序对个数是很好维护的(bit查一下就可以啦)

然后就很简单了

几分钟就写完了。。编译过了就A了


//Author: dream_maker
#include<bits/stdc++.h>
using namespace std;
//----------------------------------------------
typedef pair<int, int> pi;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define fu(a, b, c) for (int a = b; a <= c; ++a)
#define fd(a, b, c) for (int a = b; a >= c; --a)
#define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
const int INF_of_int = 1e9;
const ll INF_of_ll = 1e18;
template <typename T>
void Read(T &x) {
  bool w = 1;x = 0;
  char c = getchar();
  while (!isdigit(c) && c != '-') c = getchar();
  if (c == '-') w = 0, c = getchar();
  while (isdigit(c)) {
    x = (x<<1) + (x<<3) + c -'0';
    c = getchar();
  }
  if (!w) x = -x;
}
template <typename T>
void Write(T x) {
  if (x < 0) {
    putchar('-');
    x = -x;
  }
  if (x > 9) Write(x / 10);
  putchar(x % 10 + '0');
}
//----------------------------------------------
const int N = 1e6 + 10;
int n, m, a[N];
int nowl = 1, nowr = 0;
ll res = 0, dp[N][12];
int bit[N];

void add(int x) {
  for (; x <= n; x += x & (-x)) ++bit[x];
}

void sub(int x) {
  for (; x <= n; x += x & (-x)) --bit[x];
}

int query(int x) {
  int result = 0;
  for (; x; x -= x & (-x)) result += bit[x];
  return result;
}

int query(int l, int r) {
  return query(r) - query(l - 1);
}

void move_step(int al, int ar) {
  while (nowr < ar) {
    ++nowr;
    res += query(a[nowr], n);
    add(a[nowr]);
  }
  while (nowl > al) {
    --nowl;
    res += query(1, a[nowl]);
    add(a[nowl]);
  }
  while (nowr > ar) {
    sub(a[nowr]);
    res -= query(a[nowr], n);
    --nowr;
  }
  while (nowl < al) {
    sub(a[nowl]);
    res -= query(1, a[nowl]);
    ++nowl;
  }
}
void solve(int l, int r, int ql, int qr, int k) {
  if (l > r) return;
  int mid = (l + r) >> 1, pos = mid;
  fu(i, ql, min(qr, mid - 1)) {
    move_step(i + 1, mid);
    if (dp[i][k - 1] + res < dp[mid][k]) {
      dp[mid][k] = dp[i][k - 1] + res;
      pos = i;
    }
  }
  solve(l, mid - 1, ql, pos, k);
  solve(mid + 1, r, pos, qr, k);
}
int main() {
#ifdef dream_maker
  freopen("input.txt", "r", stdin);
#endif
  Read(n), Read(m);
  fu(i, 1, n) Read(a[i]);
  fu(i, 1, n)
    fu(j, 0, m) dp[i][j] = INF_of_ll;
  dp[0][0] = 0;
  fu(i, 1, m) solve(1, n, 0, n, i);
  Write(dp[n][m]);
  return 0;
}
posted @ 2018-11-05 20:22  Dream_maker_yk  阅读(310)  评论(0编辑  收藏  举报