[BZOJ1112] [POI2008] 砖块Klo (treap)

时间:2023-03-09 07:52:11
[BZOJ1112] [POI2008] 砖块Klo (treap)

Description

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

Input

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

Output

  最小的动作次数

Sample Input

5 3
3
9
2
3
1

Sample Output

2

HINT

  原题还要求输出结束状态时,每柱砖的高度.本题略去.

Source

Solution

  妈丫BZOJ3224的$treap$调不出来只好另做一道$treap$简单题练手了。

  欸这道题还能用树状数组做么?本蒟蒻不会丫。

  这道题很容易想到,对于每一个区间,将区间内所有数改成该区间的中位数时开销最小。

  找中位数就是找第$(k+1)/2$的数$mid$嘛,于是一个$treap$的做法就诞生了。

  滑动窗口,每次保留$k$个数,求出比$mid$小的所有数的和$sum1$以及比$mid$大的所有数的和$sum2$

  答案就是$(sum1-(比mid小的数的个数)*mid)+((比mid大的数的个数)*mid-sum2)$

  欸好像可以化简,可以化简成$sum2-sum1$,$k$是偶数时多减一个$mid$

  普通$treap$,记一个$siz$和一个$sum$即可。

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct treap
{
int l, r, siz, val, pri;
ll key, sum;
}a[];
int root, ptot, h[];
ll sum1, sum2; void push_up(int k)
{
a[k].siz = a[a[k].l].siz + a[a[k].r].siz + a[k].val;
a[k].sum = a[a[k].l].sum + a[a[k].r].sum + a[k].key * a[k].val;
} void lturn(int &k)
{
int tmp = a[k].r;
a[k].r = a[tmp].l, a[tmp].l = k;
a[tmp].siz = a[k].siz, a[tmp].sum = a[k].sum;
push_up(k), k = tmp;
} void rturn(int &k)
{
int tmp = a[k].l;
a[k].l = a[tmp].r, a[tmp].r = k;
a[tmp].siz = a[k].siz, a[tmp].sum = a[k].sum;
push_up(k), k = tmp;
} void insert(int &k, int x)
{
if(!k)
{
k = ++ptot, a[k].siz = a[k].val = ;
a[k].sum = a[k].key = x, a[k].pri = rand();
return;
}
a[k].siz++, a[k].sum += x;
if(x == a[k].key) a[k].val++;
else if(x < a[k].key)
{
insert(a[k].l, x);
if(a[k].pri < a[a[k].l].pri) rturn(k);
}
else
{
insert(a[k].r, x);
if(a[k].pri < a[a[k].r].pri) lturn(k);
}
} void del(int &k, int x)
{
if(!k) return;
if(x == a[k].key)
if(a[k].val > ) a[k].val--, a[k].siz--, a[k].sum -= x;
else if(!(a[k].l * a[k].r)) k = a[k].l + a[k].r;
else if(a[a[k].l].pri < a[a[k].r].pri) lturn(k), del(k, x);
else rturn(k), del(k, x);
else if(x < a[k].key) a[k].siz--, a[k].sum -= x, del(a[k].l, x);
else a[k].siz--, a[k].sum -= x, del(a[k].r, x);
} int query_num(int k, int x)
{
if(!k) return ;
if(x <= a[a[k].l].siz)
{
sum2 += a[a[k].r].sum + a[k].key * a[k].val;
return query_num(a[k].l, x);
}
if(x <= a[a[k].l].siz + a[k].val)
{
sum1 += a[a[k].l].sum + (x - a[a[k].l].siz - ) * a[k].key;
sum2 += a[a[k].r].sum + (a[a[k].l].siz + a[k].val - x) * a[k].key;
return a[k].key;
}
sum1 += a[a[k].l].sum + a[k].key * a[k].val;
return query_num(a[k].r, x - a[a[k].l].siz - a[k].val);
} int main()
{
int n, k, mid;
ll ans = 100000000000000LL;
cin >> n >> k, srand(n);
for(int i = ; i <= n; i++)
cin >> h[i];
for(int i = ; i <= k; i++)
insert(root, h[i]);
for(int i = k + ; i <= n + ; i++)
{
sum1 = sum2 = ;
mid = query_num(root, (k + ) >> );
ans = min(ans, sum2 - sum1 - !(k & ) * mid);
insert(root, h[i]), del(root, h[i - k]);
}
cout << ans << endl;
return ;
}