【主席树】bzoj1112: [POI2008]砖块Klo
数据结构划一下水
Description
N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:从某柱砖的顶端拿一块砖出来,丢掉不要了. 2:从仓库中拿出一块砖,放到另一柱.仓库无限大. 现在希望用最小次数的动作完成任务.
Input
第一行给出N,K. (1 ≤ k ≤ n ≤ 100000), 下面N行,每行代表这柱砖的高度.0 ≤ hi ≤ 1000000
Output
最小的动作次数
题目大意
有一个非负的数列,可以±1修改高度,求最小代价使得连续k个高度相同
题目分析
对于一个区间的答案,就相当于把所有数都放在数轴上,再求一个数使得它到所有数的总和最小。那么最优就等于是求一个区间的中位数。
于是问题相当于一个支持 求中位数;求比中位数小/大的数个数;求比中位数小/大的数总和 的数据结构。这个问题可以用主席树在$logn$内完成。
注意 printf(calc(),a,b) ,如果在calc()中改变了a,b,输出的a,b将会是改变之前的值。
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 const int maxn = 100035; 4 const int maxNode = 4000035; 5 6 struct node 7 { 8 int val,l,r; 9 ll sum; 10 }a[maxNode]; 11 ll ans,lsum,rsum,lcnt,rcnt; 12 int n,k; 13 int rt[maxn],w[maxn],cnt[maxn],tot; 14 15 int read() 16 { 17 char ch = getchar(); 18 int num = 0, fl = 1; 19 for (; !isdigit(ch); ch=getchar()) 20 if (ch=='-') fl = -1; 21 for (; isdigit(ch); ch=getchar()) 22 num = (num<<1)+(num<<3)+ch-48; 23 return num*fl; 24 } 25 void build(int &rt, int l, int r) 26 { 27 rt = ++tot; 28 if (l==r) return; 29 int mid = (l+r)>>1; 30 build(a[rt].l, l, mid); 31 build(a[rt].r, mid+1, r); 32 } 33 void update(int pre, int &rt, int l, int r, int c) 34 { 35 rt = ++tot, a[rt] = a[pre], ++a[rt].val, a[rt].sum += cnt[c]; 36 if (l==r) return; 37 int mid = (l+r)>>1; 38 if (c <= mid) update(a[pre].l, a[rt].l, l, mid, c); 39 else update(a[pre].r, a[rt].r, mid+1, r, c); 40 } 41 int query(int pre, int rt, int l, int r, int k) 42 { 43 if (l==r) return l; 44 int val = a[a[rt].l].val-a[a[pre].l].val, mid = (l+r)>>1; 45 if (val >= k){ 46 lcnt -= a[a[rt].r].val-a[a[pre].r].val; 47 lsum -= a[a[rt].r].sum-a[a[pre].r].sum; 48 return query(a[pre].l, a[rt].l, l, mid, k); 49 } 50 rcnt -= a[a[rt].l].val-a[a[pre].l].val; 51 rsum -= a[a[rt].l].sum-a[a[pre].l].sum; 52 return query(a[pre].r, a[rt].r, mid+1, r, k-val); 53 } 54 int main() 55 { 56 n = read(), k = read(), ans = 1ll<<60; 57 for (int i=1; i<=n; i++) w[i] = cnt[i] = read(); 58 std::sort(cnt+1, cnt+n+1); 59 cnt[0] = std::unique(cnt+1, cnt+n+1)-cnt-1; 60 build(rt[0], 1, cnt[0]); 61 for (int i=1; i<=n; i++) 62 { 63 w[i] = std::lower_bound(cnt+1, cnt+cnt[0]+1, w[i])-cnt; 64 update(rt[i-1], rt[i], 1, cnt[0], w[i]); 65 } 66 for (int r=k; r<=n; r++) 67 { 68 int l = r-k; 69 lcnt = rcnt = a[rt[r]].val-a[rt[l]].val, lsum = rsum = a[rt[r]].sum-a[rt[l]].sum; 70 int tmp = cnt[query(rt[l], rt[r], 1, cnt[0], (k+1)>>1)]; 71 ans = std::min(ans, 1ll*tmp*(lcnt-rcnt)-lsum+rsum); 72 } 73 printf("%lld\n",ans); 74 return 0; 75 }
END