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;
}