bzoj 1112 [POI2008]砖块Klo
[POI2008]砖块Klo
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2196 Solved: 787
[Submit][Status][Discuss]
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
3
9
2
3
1
Sample Output
2
HINT
原题还要求输出结束状态时,每柱砖的高度.本题略去.
题解:水水的一道题目,划水就可以过了。
因为区间k固定,所以每次绝对是取中位数的,
那么只需要一个数据结构,支持寻找中位数,插入,删除,
求和,这些即可。
那就是Treap瞎搞即可。
1 #include<cstring> 2 #include<cmath> 3 #include<iostream> 4 #include<algorithm> 5 #include<cstdio> 6 7 #define ll long long 8 #define ls tr[p].l 9 #define rs tr[p].r 10 #define N 100007 11 using namespace std; 12 const ll inf=2000000000000010; 13 inline ll read() 14 { 15 ll x=0,f=1;char ch=getchar(); 16 while(ch<'0'||ch>'9'){if (ch=='-')f=-1;ch=getchar();} 17 while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} 18 return x*f; 19 } 20 21 ll n,sz,rt,ans,k; 22 ll a[N]; 23 struct Node 24 { 25 ll l,r,val,siz,rnd,ct,sum;//记录左儿子,右儿子,点值,该子树大小,随机的值,该点值出现的次数。 26 }tr[N*10];//最多多少个节点,就开多少空间 27 28 inline int rand() 29 { 30 static ll seed = 2333; 31 return seed = (ll)((((seed ^ 998244353) + 19260817ll) * 19890604ll) % 1000000007); 32 } 33 inline void update(ll p) 34 { 35 tr[p].siz=tr[ls].siz+tr[rs].siz+tr[p].ct; 36 tr[p].sum=tr[ls].sum+tr[rs].sum+tr[p].ct*tr[p].val; 37 } 38 void lturn(ll &p) 39 { 40 ll t=tr[p].r;tr[p].r=tr[t].l;tr[t].l=p; 41 update(t),update(p);p=t; 42 } 43 void rturn(ll &p) 44 { 45 ll t=tr[p].l;tr[p].l=tr[t].r;tr[t].r=p; 46 tr[t].siz=tr[p].siz;update(p);p=t; 47 } 48 void ins(ll &p,ll x) 49 { 50 if (p==0) 51 { 52 p=++sz; 53 tr[p].siz=tr[p].ct=1,tr[p].val=x,tr[p].sum=x,tr[p].rnd=rand(); 54 return; 55 } 56 tr[p].siz++; 57 if (tr[p].val==x) tr[p].ct++; 58 else if (x>tr[p].val) 59 { 60 ins(tr[p].r,x); 61 if (tr[rs].rnd<tr[p].rnd) lturn(p); 62 }else 63 { 64 ins(tr[p].l,x); 65 if (tr[ls].rnd<tr[p].rnd) rturn(p); 66 } 67 update(p); 68 } 69 void del(ll &p,ll x) 70 { 71 if (p==0) return; 72 if (tr[p].val==x) 73 { 74 if (tr[p].ct>1) tr[p].ct--,tr[p].siz--,tr[p].sum-=x;//如果有多个直接减一即可。 75 else 76 { 77 if (ls==0||rs==0) p=ls+rs;//单节点或者空的话直接儿子移上来或者删去即可。 78 else if (tr[ls].rnd<tr[rs].rnd) rturn(p),del(p,x); 79 else lturn(p),del(p,x); 80 } 81 } 82 else if (x>tr[p].val) tr[p].siz--,del(rs,x),tr[p].sum-=x; 83 else tr[p].siz--,del(ls,x),tr[p].sum-=x; 84 update(p); 85 } 86 void solve(ll p,ll x,ll &sum,ll &num,ll &ge,ll &zhi) 87 { 88 if (tr[ls].siz>=x) solve(ls,x,sum,num,ge,zhi); 89 else 90 { 91 x-=tr[ls].siz,sum+=tr[ls].sum,num+=tr[ls].siz; 92 if (tr[p].ct>=x) 93 { 94 ge=tr[p].ct; 95 zhi=tr[p].val; 96 return; 97 } 98 x-=tr[p].ct,sum+=tr[p].val*tr[p].ct,num+=tr[p].ct; 99 solve(rs,x,sum,num,ge,zhi); 100 } 101 } 102 int main() 103 { 104 freopen("fzy.in","r",stdin); 105 freopen("fzy.out","w",stdout); 106 107 n=read(),k=read(); 108 for (ll i=1;i<=k;i++) 109 a[i]=read(),ins(rt,a[i]); 110 // cout<<tr[rt].val<<endl; 111 ans=inf; 112 for (ll i=k+1;i<=n+1;i++) 113 { 114 ll x=0,y=0,z=0,d=0; 115 solve(rt,(k+1)/2,x,y,z,d); 116 //cout<<x<<" "<<y<<" "<<z<<" "<<d<<endl; 117 ll res=0; 118 res+=d*y-x; 119 res+=tr[rt].sum-x-z*d-d*(tr[rt].siz-y-z); 120 ans=min(ans,res); 121 if (i<=n) 122 { 123 del(rt,a[i-k]); 124 a[i]=read(); 125 ins(rt,a[i]); 126 } 127 } 128 printf("%lld",ans); 129 }