我觉得我要改一下签名了……怎么会有窝这么啰嗦的人呢?

做这题需要先学习左偏树《左偏树的特点及其应用》 然后做一下POJ3666,这题的简单版。

思路:

考虑一下维护中位数的过程
原数组为A,找到的不降数列为B
当对于A的前n个数已经找好了最优解B[1…n],可知此时A被分成很多块,并被一些大顶堆记录,假设第i块有num个数,那么第i个堆维护这一块的最小的(num+1)/2个数,堆顶即为中位数。
假设已经处理好前7个数,被分为两块 ([a,b],c,d) ([h,e],f) (每一块按升序排列,[]中的数是堆里面维护的。
因为数列是不降的,所以b≤e
当新添加一个元素的时候,设为x,如果x≤e,将需要向前合并。
那么新的块应该是……分两种情况……
1.x>h ([h,x],e,f)
2.x<h ([x,h],e,f)

设新的中位数是val=max(h, x)  分类讨论一下可以发现改变的值是(e-val)+(val-x)

这里假设是([x,h],e,f)
当h<b时,需要继续向前合并。
合并之后是([x,h,a,b],c,d,e,f) (顺序已经不确定了,只能确定栈中元素和栈顶是b

可以发现大小为偶数的块和偶数的块合并,合并后的堆不需要弹出元素。
合并前([a,b],c,d) 的中位数是b ([x,h],e,f)的中位数是h, 合并后的中位数的b
可知答案改变都是发生在集合([x,h],e,f)中的,我们又知道b≤e(上面提到过),那么很容易得到答案是不变哒!(就是把(h-x)+(h-h)+(e-h)+(f-h)变成了(b-x)+(b-h)+(e-b)+(f-b),值是一样的

上面是偶数和偶数合并,继续讨论前一块奇数和后一块偶数合并。
设前一块是([a,b],c) 中位数是b,后一块是([d,e],f,g)中位数是e,合并后不需要弹出,中位数是b,类似上面的情况,我们可以得出b≤f,所以答案仍然不变。

前一块偶数,后一块奇数
([a,b],c,d)中位数是b ([e,f],g)中位数是f 合并后不需要弹出 中位数是b 其中(f<b≤g)
那么答案由([e,f],g)的改变产生,f的左右两边是可以抵消掉的,改变只会因为f,改变的值是b-f

前一块奇数,后一块奇数
设前一块是([a,b],c) 中位数是b,后一块是([d,e],f,)中位数是e 其中e<b≤f
合并后弹出元素b,中位数为max(a,e),设为val
那么答案改变就是b-e

到此,所有情况都讨论完了ˊ_>ˋ

结论:当一个块和前面的块合并时,如果当前块的数量为偶数,答案不变,否则答案增加(前一块的中位数-当前块的中位数)

代码:

/*****************************************
Problem: 3016 User: G_lory
Memory: 12676K Time: 1797MS
Language: G++ Result: Accepted
*****************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; const int N = ;
const int INF = 0x5f5f5f5f;
typedef long long ll; struct LTree {
int l, r, sz;
int key, dis;
bool operator<(const LTree lt) const {
return key < lt.key;
}
} tr[N];
int cnt_tr; int NewTree(int k) {
tr[++cnt_tr].key = k;
tr[cnt_tr].l = tr[cnt_tr].r = tr[cnt_tr].dis = ;
tr[cnt_tr].sz = ;
return cnt_tr;
} int Merge(int x, int y) {
if (!x || !y) return x + y;
if (tr[x] < tr[y]) swap(x, y);
tr[x].r = Merge(tr[x].r, y);
if (tr[tr[x].l].dis < tr[tr[x].r].dis) swap(tr[x].l, tr[x].r);
tr[x].dis = tr[tr[x].r].dis + ;
tr[x].sz = tr[tr[x].l].sz + tr[tr[x].r].sz + ;
return x;
} int Top(int x) {
return tr[x].key;
} void Pop(int &x) {
x = Merge(tr[x].l, tr[x].r);
} int root[N], num[N];
void cal(int a[], int n, int ans[]) {
int res;
cnt_tr = res = ;
int cnt = ;
for (int i = ; i < n; ++i) {
root[++cnt] = NewTree(a[i]);
num[cnt] = ;
while (cnt > && Top(root[cnt]) < Top(root[cnt-])) {
cnt--;
if (num[cnt+]&) res += Top(root[cnt]) - Top(root[cnt+]);
root[cnt] = Merge(root[cnt], root[cnt+]);
num[cnt] += num[cnt+];
while (tr[root[cnt]].sz* > num[cnt]+) {
Pop(root[cnt]);
}
int now = Top(root[cnt]); }
ans[i] = res;
}
} int a[N], b[N], c[N];
int in[N][N], de[N][N];
int dp[N][N];
int main() {
//freopen("in", "r", stdin);
int n, k;
while (~scanf("%d%d",&n, &k) && n) {
for (int i = ; i <= n; ++i) {
scanf("%d", a+i);
b[i] = a[i]-i;
c[i] = -a[i]-i;
}
for (int i = ; i <= n; ++i) {
cal(c+i, n-i+, de[i]+i);
cal(b+i, n-i+, in[i]+i);
}
for (int i = ; i <= n; ++i) dp[][i] = INF;
for (int i = ; i <= k; ++i) {
dp[i][] = ;
for (int j = ; j <= n; ++j) {
dp[i][j] = INF;
for (int p = ; p < j; ++p) {
dp[i][j] = min(dp[i][j], dp[i-][p] + min(in[p+][j], de[p+][j]));
}
}
}
printf("%d\n", dp[k][n]);
}
return ;
}

  

POJ3016-K-Monotonic(左偏树+DP)的更多相关文章

  1. 左偏树(DP)问题

    问题:A straight dirt road connects two fields on FJ's farm, but it changes elevation more than FJ woul ...

  2. 『左偏树 Leftist Tree』

    新增一道例题 左偏树 Leftist Tree 这是一个由堆(优先队列)推广而来的神奇数据结构,我们先来了解一下它. 简单的来说,左偏树可以实现一般堆的所有功能,如查询最值,删除堆顶元素,加入新元素等 ...

  3. Monkey King(左偏树 可并堆)

    我们知道如果要我们给一个序列排序,按照某种大小顺序关系,我们很容易想到优先队列,的确很方便,但是优先队列也有解决不了的问题,当题目要求你把两个优先队列合并的时候,这就实现不了了 优先队列只有插入 删除 ...

  4. POJ3666-Making the Grade(左偏树 or DP)

    左偏树 炒鸡棒的论文<左偏树的特点及其应用> 虽然题目要求比论文多了一个条件,但是……只需要求非递减就可以AC……数据好弱…… 虽然还没想明白为什么,但是应该觉得应该是这样——求非递减用大 ...

  5. k短路模板(洛谷P2483 [SDOI2010]魔法猪学院)(k短路,最短路,左偏树,priority_queue)

    你谷数据够强了,以前的A*应该差不多死掉了. 所以,小伙伴们快来一起把YL顶上去把!戳这里! 俞鼎力的课件 需要掌握的内容: Dijkstra构建最短路径树. 可持久化堆(使用左偏树,因其有二叉树结构 ...

  6. 洛谷P1552 [APIO2012] 派遣 [左偏树,树形DP]

    题目传送门 忍者 Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都 ...

  7. Luogu P1552 [APIO2012]派遣【左偏树】By cellur925

    题目传送门 $Chat$ 哈哈哈我xj用dfs序乱搞竟然炸出了66分....(其实还是数据水,逃) $Sol$ 首先我们应该知道,一个人他自己的满意度与他子树所有节点的领导力是无关的,一个人的满意度受 ...

  8. BZOJ 1455 罗马游戏 ——左偏树

    [题目分析] 左偏树的模板题目,大概就是尽量维护树的深度保持平衡,以及尽可能的快速合并的一种堆. 感觉和启发式合并基本相同. 其实并没有快很多. 本人的左偏树代码自带大常数,借鉴请慎重 [代码] #i ...

  9. 【BZOJ-1455】罗马游戏 可并堆 (左偏树)

    1455: 罗马游戏 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1355  Solved: 561[Submit][Status][Discuss] ...

随机推荐

  1. C++ DLL 模板 .

    C++ DLL 模板 1.使用VS2005创建Win32 DLL项目,选择空项目,然后加入CppDll.h和CppDll.cpp文件. 2.修改CppDll.h和CppDll.cpp文件使之成为需要的 ...

  2. ANDROID_MARS学习笔记_S01_008Linear_layout例子

    1.netstone_layout.xml <?xml version="1.0" encoding="utf-8"?> <LinearLay ...

  3. Oracle 学习笔记 常用查询命令篇

    1.查询某个用户下有多少张表 有时候很有用  select count(*) from dba_tables t where t.owner='SCOTT';

  4. ripple

     ripple模拟器非常好用,chrome上的插件 

  5. ARM7ldr指令与ldr伪指令

    ldr伪指令的第二个操作数之前有个=,意思是第一个操作书 = 第二个操作数,相当明了 核心就在于对于用.word指令在.text段里另外定义一段内存,用ldr r0,[pc + x(可以算出.text ...

  6. LINUX进程优先级实现

    首先linux进程优先级的范围是-20到19 将当前目录下的documents目录打包,但不希望tar占用太多CPU: nice -19 tar zcf pack.tar.gz documents 这 ...

  7. android 引用 project以及下拉刷新开源类库Android-PullToRefresh 的使用

    Android-PullToRefresh 是一个github上的开源下拉刷新类库, GitHub  .此外,该作者还有另外一个实用度和关注量极高的项目–另一种Android ActionBar的实现 ...

  8. System.Drawing.Design.UITypeEditor自定义控件属性GetEditStyle(ITypeDescriptorContext context),EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.C ...

  9. Oracle Length 和 Lengthb 函数说明 .(用来判断记录值里是否有中文内容)

    一.官网的说明 http://download.oracle.com/docs/cd/E11882_01/server.112/e26088/functions088.htm#SQLRF00658 P ...

  10. java vm args

    这个问题主要还是由这个问题 java.lang.OutOfMemoryError: Java heap space 引起的.第一次出现这样的的问题以后,引发了其他的问题.在网上一查可能是JAVA的堆栈 ...