标签:
新博客宣传一发http://shijieyywd.com/?p=73
给定一个序列
第1行为一个整数
一个整数R
Sample Input
7
9
4
8
20
14
15
18
13
所求的Z序列为6, 7, 8, 13, 14, 15, 18.
R=13
论文:左偏树的特点及其应用
现学了一波左偏树orz.
先考虑只要求
两个特殊情况:
-
-
于是可以将原数列划分成m个区间,每一段的解为该区间的中位数。
实现:
假设已经求出了前k个数的最优解,被划分成了m个区间,每段区间的最优解为
如何维护中位数:当堆的大小大于区间长度的一半时删除堆顶元素,则堆中的元素一定是该区间内较小的一半元素,堆顶元素即为该区间的中位数。
这只是
然而要求递增只需要将原本的t[i]改成t[i] - i,再按照上述做法做就行了orz。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000005;
int n;
int arr[MAXN];
int tot = 0;
int v[MAXN], tr[MAXN][2], dist[MAXN], siz[MAXN];
int root[MAXN];
int l[MAXN], r[MAXN];
int merge(int x, int y) {
if (!x || !y) return x + y;
if (v[x] < v[y]) swap(x, y);
tr[x][1] = merge(tr[x][1], y);
siz[x] = siz[tr[x][0]] + siz[tr[x][1]] + 1;
if (dist[tr[x][1]] > dist[tr[x][0]]) swap(tr[x][0], tr[x][1]);
dist[x] = dist[tr[x][1]] + 1;
return x;
}
int top(int x) {
return v[x];
}
int size(int x) {
return siz[x];
}
void pop(int &x) {
x = merge(tr[x][0], tr[x][1]);
}
int newNode(int x) {
v[++tot] = x;
siz[tot] = 1;
tr[tot][0] = tr[tot][1] = dist[tot] = 0;
return tot;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &arr[i]);
arr[i] -= i;
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
cnt++;
root[cnt] = newNode(arr[i]);
l[cnt] = r[cnt] = i;
while (cnt > 1 && top(root[cnt]) < top(root[cnt - 1])) {
cnt--;
root[cnt] = merge(root[cnt], root[cnt + 1]);
r[cnt] = r[cnt + 1];
while (size(root[cnt]) * 2 > r[cnt] - l[cnt] + 2)
pop(root[cnt]);
}
}
long long ans = 0;
for (int i = 1; i <= cnt; i++) {
int t = top(root[i]);
for (int j = l[i]; j <= r[i]; j++) {
ans += abs(t - arr[j]);
}
}
printf("%lld\n", ans);
return 0;
}
bzoj1367 [Baltic2004]sequence [左偏树]
标签:
原文地址:http://blog.csdn.net/u011265346/article/details/46532421