[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$即可。

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 struct treap
  5 {
  6     int l, r, siz, val, pri;
  7     ll key, sum;
  8 }a[100005];
  9 int root, ptot, h[100005];
 10 ll sum1, sum2;
 11  
 12 void push_up(int k)
 13 {
 14     a[k].siz = a[a[k].l].siz + a[a[k].r].siz + a[k].val;
 15     a[k].sum = a[a[k].l].sum + a[a[k].r].sum + a[k].key * a[k].val;
 16 }
 17  
 18 void lturn(int &k)
 19 {
 20     int tmp = a[k].r;
 21     a[k].r = a[tmp].l, a[tmp].l = k;
 22     a[tmp].siz = a[k].siz, a[tmp].sum = a[k].sum;
 23     push_up(k), k = tmp;
 24 }
 25  
 26 void rturn(int &k)
 27 {
 28     int tmp = a[k].l;
 29     a[k].l = a[tmp].r, a[tmp].r = k;
 30     a[tmp].siz = a[k].siz, a[tmp].sum = a[k].sum;
 31     push_up(k), k = tmp;
 32 }
 33  
 34 void insert(int &k, int x)
 35 {
 36     if(!k)
 37     {
 38         k = ++ptot, a[k].siz = a[k].val = 1;
 39         a[k].sum = a[k].key = x, a[k].pri = rand();
 40         return;
 41     }
 42     a[k].siz++, a[k].sum += x;
 43     if(x == a[k].key) a[k].val++;
 44     else if(x < a[k].key)
 45     {
 46         insert(a[k].l, x);
 47         if(a[k].pri < a[a[k].l].pri) rturn(k);
 48     }
 49     else
 50     {
 51         insert(a[k].r, x);
 52         if(a[k].pri < a[a[k].r].pri) lturn(k);
 53     }
 54 }
 55  
 56 void del(int &k, int x)
 57 {
 58     if(!k) return;
 59     if(x == a[k].key)
 60         if(a[k].val > 1) a[k].val--, a[k].siz--, a[k].sum -= x;
 61         else if(!(a[k].l * a[k].r)) k = a[k].l + a[k].r;
 62         else if(a[a[k].l].pri < a[a[k].r].pri) lturn(k), del(k, x);
 63         else rturn(k), del(k, x);
 64     else if(x < a[k].key) a[k].siz--, a[k].sum -= x, del(a[k].l, x);
 65     else a[k].siz--, a[k].sum -= x, del(a[k].r, x);
 66 }
 67  
 68 int query_num(int k, int x)
 69 {
 70     if(!k) return 0;
 71     if(x <= a[a[k].l].siz)
 72     {
 73         sum2 += a[a[k].r].sum + a[k].key * a[k].val;
 74         return query_num(a[k].l, x);
 75     }
 76     if(x <= a[a[k].l].siz + a[k].val)
 77     {
 78         sum1 += a[a[k].l].sum + (x - a[a[k].l].siz - 1) * a[k].key;
 79         sum2 += a[a[k].r].sum + (a[a[k].l].siz + a[k].val - x) * a[k].key;
 80         return a[k].key;
 81     }
 82     sum1 += a[a[k].l].sum + a[k].key * a[k].val;
 83     return query_num(a[k].r, x - a[a[k].l].siz - a[k].val);
 84 }
 85  
 86 int main()
 87 {
 88     int n, k, mid;
 89     ll ans = 100000000000000LL;
 90     cin >> n >> k, srand(n);
 91     for(int i = 1; i <= n; i++)
 92         cin >> h[i];
 93     for(int i = 1; i <= k; i++)
 94         insert(root, h[i]);
 95     for(int i = k + 1; i <= n + 1; i++)
 96     {
 97         sum1 = sum2 = 0;
 98         mid = query_num(root, (k + 1) >> 1);
 99         ans = min(ans, sum2 - sum1 - !(k & 1) * mid);
100         insert(root, h[i]), del(root, h[i - k]);
101     }
102     cout << ans << endl;
103     return 0;
104 }
View Code

 

posted @ 2016-05-01 00:35  CtrlCV  阅读(187)  评论(0编辑  收藏  举报