[bzoj1112][POI2008]磚塊Klo_非旋轉Treap
阿新 • • 發佈:2019-01-07
磚塊Klo bzoj-1112 POI-2008
題目大意:$N$柱磚,希望有連續$K$柱的高度是一樣的. 你可以選擇以下兩個動作 1:從某柱磚的頂端拿一塊磚出來,丟掉不要了. 2:從倉庫中拿出一塊磚,放到另一柱.倉庫無限大. 現在希望用最小次數的動作完成任務.
註釋:$1\le k\le n\le 10^5$,$0\le height_i\le 10^6$。
想法:
如果我們想讓以$i$為左端點的連續$k$柱合法,
假設最後的高度是$H$,那麼我們的代價就是:
$\sum\limits_{j=i}^{i+k-1} |h_j-H|$。
顯然當$H$為這$k$個數的中位數的時候當前區間的代價最小。
至此,我們就可以弄一個平衡樹。
每次加入$i+k$然後把開頭的$i$刪去,求出當前區間的中位數更新答案即可。
Code:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define N 100010 using namespace std; typedef long long ll; struct Node { int ls,rs,size; ll sum,val,key; }a[N]; struct par {int x,y;}; ll num[N]; int root,cnt; inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;} ll rd() {ll x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=nc(); return x;} inline int newnode(ll val) { int x=++cnt; a[x].size=1; a[x].ls=a[x].rs=0; a[x].val=a[x].sum=val; a[x].key=rand()*rand(); return x; } inline void pushup(int x) { int ls=a[x].ls,rs=a[x].rs; a[x].size=1; a[x].sum=a[x].val; if(ls) a[x].size+=a[ls].size,a[x].sum+=a[ls].sum; if(rs) a[x].size+=a[rs].size,a[x].sum+=a[rs].sum; } int merge(int x,int y) { if(!x||!y) return x|y; if(a[x].key>a[y].key) { a[x].rs=merge(a[x].rs,y); pushup(x); return x; } else { a[y].ls=merge(x,a[y].ls); pushup(y); return y; } } par split(int x,int k) { if(!k) return (par){0,x}; int ls=a[x].ls,rs=a[x].rs; if(k==a[ls].size) { a[x].ls=0; pushup(x); return (par){ls,x}; } else if(k==a[ls].size+1) { a[x].rs=0; pushup(x); return (par){x,rs}; } else if(k<a[ls].size) { par t=split(ls,k); a[x].ls=t.y; pushup(x); return (par){t.x,x}; } else { par t=split(rs,k-a[ls].size-1); a[x].rs=t.x; pushup(x); return (par){x,t.y}; } } par split_val(int x,int val) { if(!x) return (par){0,x}; int ls=a[x].ls,rs=a[x].rs; if(a[x].val>=val) { par t=split_val(ls,val); a[x].ls=t.y; pushup(x); return (par){t.x,x}; } else { par t=split_val(rs,val); a[x].rs=t.x; pushup(x); return (par){x,t.y}; } } void delet(ll val) { par t1=split_val(root,val),t2=split(t1.y,1); root=merge(t1.x,t2.y); } void insert(ll val) { par t1=split_val(root,val); root=merge(t1.x,merge(newnode(val),t1.y)); } void output(int x) { int ls=a[x].ls,rs=a[x].rs; if(ls) output(ls); printf("%lld ",a[x].val); if(rs) output(rs); } int main() { srand(12378); int n=rd(),k=rd(); for(int i=1;i<=n;i++) num[i]=rd(); for(int i=1;i<=k;i++) insert(num[i]); int id=(k+1)/2; par t1=split(root,id-1),t2=split(t1.y,1); ll ans=(a[t2.x].val*a[t1.x].size-a[t1.x].sum)+(a[t2.y].sum-a[t2.x].val*a[t2.y].size); int dic=k,dic_val=a[t2.x].val; root=merge(t1.x,merge(t2.x,t2.y)); for(int i=k+1;i<=n;i++) { insert(num[i]); delet(num[i-k]); // printf("%d : ",i); output(root); puts(""); par t1=split(root,id-1),t2=split(t1.y,1); ll now=(a[t2.x].val*a[t1.x].size-a[t1.x].sum)+(a[t2.y].sum-a[t2.x].val*a[t2.y].size); if(now<ans) dic=i,ans=now,dic_val=a[t2.x].val; root=merge(t1.x,merge(t2.x,t2.y)); } for(int i=dic-k+1;i<=dic;i++) num[i]=dic_val; printf("%lld\n",ans); // for(int i=1;i<=n;i++) printf("%lld\n",num[i]); // puts(""); return 0; }
小結:平衡樹的應用還是較為廣泛的。