[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 }

时间: 2024-10-10 23:55:39

[BZOJ1112] [POI2008] 砖块Klo (treap)的相关文章

bzoj1112[POI2008]砖块Klo*

bzoj1112[POI2008]砖块Klo 题意: N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:丢掉某柱砖的一块砖.给某柱加上一块砖,现在希望用最小次数的动作完成任务.N≤100000 题解: 设一个区间长度为k,其中位数为a,比a小的元素个数为b,和为c:比a大的元素个数为d,和为e.则题目要求维护一个长度为k的滑动窗口,能求出它的b*a-c+e-d*a.故用一个维护sum,size两个值的treap来维护.然而似乎我想复杂了?比所有人代码都大1k!注意要开long

【bzoj1112】[POI2008]砖块Klo Treap

题目描述 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的中位数. 那么我们可

【枚举】【权值分块】bzoj1112 [POI2008]砖块Klo

枚举长度为m的所有段,尝试用中位数更新答案. 所以需要数据结构,支持查询k大,以及大于/小于 k大值 的数的和. 平衡树.权值线段树.权值分块什么的随便呢. 1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 using namespace std; 5 typedef long long ll; 6 struct Point{int v,p;}t[100001]; 7 bool operator &

BZOJ 1112: [POI2008]砖块Klo

1112: [POI2008]砖块Klo Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1736  Solved: 606[Submit][Status][Discuss] Description N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:从某柱砖的顶端拿一块砖出来,丢掉不要了. 2:从仓库中拿出一块砖,放到另一柱.仓库无限大. 现在希望用最小次数的动作完成任务. Input 第一行给出N,K. (1 ≤ k ≤ n ≤

1112: [POI2008]砖块Klo

1112: [POI2008]砖块Klo Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1245  Solved: 426[Submit][Status][Discuss] Description N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:从某柱砖的顶端拿一块砖出来,丢掉不要了. 2:从仓库中拿出一块砖,放到另一柱.仓库无限大. 现在希望用最小次数的动作完成任务. Input 第一行给出N,K. (1 ≤ k ≤ n ≤

BZOJ 1112 [POI2008]砖块Klo(可持久化线段树)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1112 [题目大意] 给出一个数列,对于一个操作,你可以对一个数+1,或者一个数-1, 问若使得数列中出现长度为m的连续相同的数,最少需要的操作数. [题解] 我们发现对于固定区间求最小操作,等价于求区间中数距离中位数差值和最小, 我们发现区间中位数可以利用主席树求区间kth来实现, 同时在主席树上维护权值线段树的区间真值和,那么对于每个区间中的数, 就能分别维护比中位数小的部分的和以

[BZOJ 1112] [POI2008] 砖块Klo 【区间K大】

题目链接:BZOJ - 1112 题目分析 枚举每一个长度为k的连续区间,求出这个区间的最优答案,更新全局答案. 可以发现,这个区间的所有柱子最终都变成这k个数的中位数时最优,那么我们就需要查询这个区间的中位数了. 找到中位数之后,我们还应该求出这个区间内小于中位数的数的和,大于中位数的数的和,从而求出操作步数. 这些需要求的值可以用线段树或平衡树来写,我写的是线段树,但是实际上这是一道POI的题目,在MAIN上的空间限制只有35MB,线段树应该是不行的. 因为平衡树只需要 O(n) 空间,所以

BZOJ 1112: [POI2008]砖块Klo1112( BST )

枚举每个长度为k的区间, 然后用平衡树找中位数进行判断, 时间复杂度O(nlogn). 早上起来精神状态不太好...连平衡树都不太会写了...果断去看了会儿番然后就A了哈哈哈 -------------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; i++) #define clr(x,

bzoj-1112 砖块Klo

题意: 给出一个长度为n的数列,现要将其连续k个数变成一样的: 每次可以对一个数+1或-1,问最小操作次数: 1<=k<=n<=100000,0<=数列中的数<=1000000: 题解: 感觉是一道好题吧: 首先有这样一个结论:将这些数置为中位数所需要的操作数最小: 证明啥的网上关于中位数的一大堆?反正我不会: 然后我们要做的就是: 1.动态维护区间中位数: 2.对一个区间更新答案: 有的同学选择了平衡树解决.. 实际上这个题用权值树状数组就好了,虽说多个log= =: 中位