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

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

  1. BZOJ5125: [Lydsy1712月赛]小Q的书架(DP决策单调性)

    题意:N个数,按顺序划分为K组,使得逆序对之和最小. 思路:之前能用四边形不等式写的,一般网上都还有DP单调性分治的做法,今天也尝试用后者写(抄)了一遍.即: 分成K组,我们进行K-1次分治,get( ...

  2. [BZOJ5125]小Q的书架(决策单调性+分治DP+树状数组)

    显然有决策单调性,但由于逆序对不容易计算,考虑分治DP. solve(k,x,y,l,r)表示当前需要选k段,待更新的位置为[l,r],这些位置的可能决策点区间为[x,y].暴力计算出(l+r)/2的 ...

  3. bzoj 5125: [Lydsy1712月赛]小Q的书架

    新学了一波 决策单调性 dp 套路.... 这种dp一般是长这样的 => f[i][j] = max/min  { f[i-1][k] + cost(k+1,j)} ,其中cost函数满足四边形 ...

  4. 决策单调性优化dp

    决策单调性: 对于一些dp方程,经过一系列的猜想和证明,可以得出,所有取的最优解的转移点(即决策点)位置是单调递增的. 即:假设f[i]=min(f[j]+b[j]) (j<i) 并且,对于任意 ...

  5. 2018.09.28 bzoj1563: [NOI2009]诗人小G(决策单调性优化dp)

    传送门 决策单调性优化dp板子题. 感觉队列的写法比栈好写. 所谓决策单调性优化就是每次状态转移的决策都是在向前单调递增的. 所以我们用一个记录三元组(l,r,id)(l,r,id)(l,r,id)的 ...

  6. Lightning Conductor 洛谷P3515 决策单调性优化DP

    遇见的第一道决策单调性优化DP,虽然看了题解,但是新技能√,很开森. 先%FlashHu大佬,反正我是看了他的题解和精美的配图才明白的,%%%巨佬. 废话不多说,看题: 题目大意 已知一个长度为n的序 ...

  7. [BZOJ4850][JSOI2016]灯塔(分块/决策单调性优化DP)

    第一种方法是决策单调性优化DP. 决策单调性是指,设i>j,若在某个位置x(x>i)上,决策i比决策j优,那么在x以后的位置上i都一定比j优. 根号函数是一个典型的具有决策单调性的函数,由 ...

  8. CF868F Yet Another Minimization Problem 分治决策单调性优化DP

    题意: 给定一个序列,你要将其分为k段,总的代价为每段的权值之和,求最小代价. 定义一段序列的权值为$\sum_{i = 1}^{n}{\binom{cnt_{i}}{2}}$,其中$cnt_{i}$ ...

  9. BZOJ2216 Poi2011 Lightning Conductor 【决策单调性优化DP】

    Description 已知一个长度为n的序列a1,a2,...,an. 对于每个1<=i<=n,找到最小的非负整数p满足 对于任意的j, aj < = ai + p - sqrt( ...

随机推荐

  1. Java 强引用、软引用、弱引用、幻象引用有什么区别

    1)引用出现的根源 引用出现的根源是由于GC内存回收的基本原理.GC回收本质上是回收对象.目前比较流行的回收算法是可达性分析算法.从GC roots开始安装一定的逻辑判断一个对象是否可达,不可达的话就 ...

  2. MySQL connector c++使用笔记

    MySQL的connector官方地址: http://dev.mysql.com/downloads/connector/ 针对c++来说, 可以选择c或者c++的库. c++的实现是参考了java ...

  3. springcloud18---springCloudConfig

    package com.itmuch.cloud; import org.springframework.beans.factory.annotation.Value; import org.spri ...

  4. 一次频繁Full GC问题排查过程分享

    问题描述 应用收到频繁Full GC告警 问题排查 登录到对应机器上去,查看GC日志,发现YGC一分钟已经达到了15次,比Full GC还要频繁一些,其中Full GC平均10分钟超过了4次,如下图 ...

  5. oracle 11g 数据库中报:协议适配器错误

    本人遇到该问题,到数据库服务器上重新启动监听和实例就OK了.

  6. 20145315 《Java程序设计》第七周学习总结

    20145315 <Java程序设计>第七周学习总结 教材学习内容总结 第十三章 时间与日期 13.1.1时间的度量 1.格林威治时间(GMT):参考太阳到达最高点,有时间误差. 2.世界 ...

  7. MVC中定时发布二维码邮件

    发布邮件 查看第一个方法就可以了,第二个跟这个无关 using System; using System.Collections.Generic; using System.Linq; using S ...

  8. [参考]ASCII对照表 及 字符与二进制、十进制、16进制之间的转化(C/C++)

    第1节 ASCII码对照表 1.1 ASCII控制字符 1.2 ASCII可显示字符 第2节字符的进制转换 2.1 获取字符(8位)的上四位和下四位 2.2 获取字符(上表中的‘图形’)所对应的十六进 ...

  9. Spring ApplicationListener使用方法及二次调用问题解决

    使用场景 在一些业务场景中,当容器初始化完成之后,需要处理一些操作,比如一些数据的加载.初始化缓存.特定任务的注册等等.这个时候我们就可以使用Spring提供的ApplicationListener来 ...

  10. python 随机整数

    # Program to generate a random number between and # import the random module import random print(ran ...