您的位置:首页 > 其它

【bzoj1112】[POI2008]砖块Klo Treap

2017-04-01 14:31 453 查看
题目描述

N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:从某柱砖的顶端拿一块砖出来,丢掉不要了. 2:从仓库中拿出一块砖,放到另一柱.仓库无限大. 现在希望用最小次数的动作完成任务.

输入

第一行给出N,K. (1 ≤ k ≤ n ≤ 100000), 下面N行,每行代表这柱砖的高度.0 ≤ hi ≤ 1000000

输出

最小的动作次数

样例输入

5 3

3

9

2

3

1

样例输出

2

题解

Treap

根据某数学定理,∑|ai-x|最小时x为ai的中位数。

那么我们可以对每段可能的区间取一下中位数,再查找这个最小和。

具体实现方法可以使用Treap,同时维护si和sum。

求最小和时需要递归进行,比较x和v[k]的大小关系,如果有一整颗子树的值都小于或大于x,那么直接加上|sum-si*x|,并递归处理剩余部分。

时间好像有点长,大概5s,但这样的做法保证能过。

#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long ll;
int cnt[100010] , rnd[100010] , l[100010] , r[100010] , si[100010] , root , tot;
ll a[100010] , v[100010] , sum[100010];
void pushup(int x)
{
si[x] = si[l[x]] + si[r[x]] + cnt[x];
sum[x] = sum[l[x]] + sum[r[x]] + v[x] * cnt[x];
}
void zig(int &k)
{
int t = l[k];
l[k] = r[t];
r[t] = k;
pushup(k);
pushup(t);
k = t;
}
void zag(int &k)
{
int t = r[k];
r[k] = l[t];
l[t] = k;
pushup(k);
pushup(t);
k = t;
}
void ins(int &k , ll x)
{
if(!k) k = ++tot , v[k] = sum[k] = x , cnt[k] = si[k] = 1 , rnd[k] = rand();
else if(x == v[k]) cnt[k] ++ ;
else if(x < v[k])
{
ins(l[k] , x);
if(rnd[l[k]] < rnd[k]) zig(k);
}
else
{
ins(r[k] , x);
if(rnd[r[k]] > rnd[k]) zag(k);
}
pushup(k);
}
void del(int &k , ll x)
{
if(x == v[k] && cnt[k] <= 1)
{
if(l[k] * r[k] == 0) k = l[k] + r[k];
else if(rnd[l[k]] < rnd[r[k]]) zig(k) , del(k , x);
else zag(k) , del(k , x);
return;
}
if(x == v[k]) cnt[k] -- ;
else if(x < v[k]) del(l[k] , x);
else del(r[k] , x);
pushup(k);
}
ll find(int k , int x)
{
if(x <= si[l[k]]) return find(l[k] , x);
else if(x > si[l[k]] + cnt[k]) return find(r[k] , x - si[l[k]] - cnt[k]);
else return v[k];
}
ll query(int k , ll x)
{
if(!k) return 0;
if(x == v[k]) return x * si[l[k]] - sum[l[k]] + sum[r[k]] - x * si[r[k]];
else if(x < v[k]) return (v[k] - x) * cnt[k] + sum[r[k]] - x * si[r[k]] + query(l[k] , x);
else return (x - v[k]) * cnt[k] + x * si[l[k]] - sum[l[k]] + query(r[k] , x);
}
int main()
{
int n , k , i;
ll ans = 0x7fffffffffffffffll;
scanf("%d%d" , &n , &k);
for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &a[i]);
for(i = 1 ; i < k ; i ++ ) ins(root , a[i]);
for(i = k ; i <= n ; i ++ )
{
ins(root , a[i]);
ans = min(ans , query(root , find(root , k / 2 + 1))) , del(root , a[i - k + 1]);
}
printf("%lld\n" , ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: