1. 程式人生 > >bzoj 1112 poi 2008 磚塊

bzoj 1112 poi 2008 磚塊

line stack upd sin 最優解 -- 幾何 def col

這滯脹題調了兩天了...

好愚蠢的錯誤啊...

其實這道題思維比較簡單,就是利用treap進行維護(有人說線段樹好寫,表示treap真心很模板)

就是枚舉所有長度為k的區間,查出中位數,計算代價即可。

(根據絕對值不等式的幾何意義,中位數一定是最優解)

而維護長度為k的區間也很簡單,就是首先把前k個扔到樹上,然後每次把新來的插入,把最前面的一個刪除即可

至於求中位數,簡直就是基礎操作嘛

關鍵在於...代價怎麽算?

顯然我們不能把所有數枚舉出來挨個加減,這樣會T飛的...

所以我們考慮直接在treap上維護,根據treap很重要的性質:左樹<根<右樹

那麽我們對每個節點,維護一個子樹權值和,這樣就可以做到在查詢中位數的同時查出小於中位數的數之和和大於中位數的數之和了

註意一個小細節,就是在查詢的時候,要把重復出現的中位數分左右放到左右的和裏,否則計算會有bug

剩下的就是模板了

不要像我一樣,插點不修改樹的大小,輸出全是負數...

貼代碼(巨醜)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ls tree[rt].lson
#define
rs tree[rt].rson #define ll long long using namespace std; struct Treap { int lson; int rson; int huge; int same; ll val; int rank; ll sum; }tree[100005]; int a[100005]; int tot=0; int n,k,mid; ll s[100005]; int rot=0; inline int read() { int f=1,x=0;char ch=getchar(); while(ch<
0||ch>9){if(ch==-)f=-1;ch=getchar();} while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();} return x*f; } void update(int rt) { tree[rt].huge=tree[ls].huge+tree[rs].huge+tree[rt].same; tree[rt].sum=(ll)tree[ls].sum+(ll)tree[rs].sum+(ll)tree[rt].same*(ll)tree[rt].val; } void lturn(int &rt) { int temp=rs; rs=tree[rs].lson; tree[temp].huge=tree[rt].huge; tree[temp].sum=tree[rt].sum; tree[temp].lson=rt; update(rt); rt=temp; } void rturn(int &rt) { int temp=ls; ls=tree[ls].rson; tree[temp].huge=tree[rt].huge; tree[temp].rson=rt; tree[temp].sum=tree[rt].sum; update(rt); rt=temp; } void ins(int &rt,ll v) { if(!rt) { rt=++tot; tree[rt].huge=1; tree[rt].same=1; tree[rt].val=v; tree[rt].rank=rand(); tree[rt].sum=v; return; } tree[rt].sum+=v; tree[rt].huge++; if(tree[rt].val==v) { tree[rt].same++; return; } if(tree[rt].val>v) { ins(ls,v); if(tree[ls].rank<tree[rt].rank) { rturn(rt); } }else { ins(rs,v); if(tree[rs].rank<tree[rt].rank) { lturn(rt); } } } void del(int &rt,ll v) { if(!rt) { return; } if(tree[rt].val==v) { if(tree[rt].same>1) { tree[rt].huge--; tree[rt].same--; tree[rt].sum-=(ll)v; return; }else if(ls*rs==0) { rt=ls+rs; return; }else { if(tree[ls].rank<tree[rs].rank) { rturn(rt); del(rt,v); }else { lturn(rt); del(rt,v); } } return; } tree[rt].huge--; tree[rt].sum-=v; if(tree[rt].val>v) { del(ls,v); }else { del(rs,v); } update(rt); } ll Lsum,Rsum; int tt; int query_num(int rt,int v) { if(!rt) { return 0; } if(tree[ls].huge>=v) { Rsum+=(ll)tree[rs].sum+(ll)tree[rt].same*(ll)tree[rt].val; return query_num(ls,v); }else if(tree[ls].huge+tree[rt].same<v) { Lsum+=(ll)tree[ls].sum+(ll)tree[rt].val*(ll)tree[rt].same; return query_num(rs,v-tree[ls].huge-tree[rt].same); }else { Lsum+=(ll)tree[ls].sum+(ll)(v-tree[ls].huge-1)*(ll)tree[rt].val; Rsum+=(ll)tree[rs].sum+(ll)(tree[ls].huge+tree[rt].same-v)*(ll)tree[rt].val; return tree[rt].val; } } int main() { n=read(),k=read(); mid=(k+1)/2; for(int i=1;i<=n;i++) { a[i]=read(); } for(int i=1;i<=k;i++) { ins(rot,a[i]); } int lret=1,rret=k; int v0=query_num(rot,mid); int ret=v0; ll co=(ll)(mid-1)*(ll)v0-Lsum+Rsum-(ll)(k-mid)*(ll)v0; for(int i=k+1;i<=n;i++) { int st=i-k+1; del(rot,a[st-1]); ins(rot,a[i]); tt=0,Lsum=0,Rsum=0; int v1=query_num(rot,mid); ll temp=(ll)(mid-1)*(ll)v1-Lsum+Rsum-(ll)(k-mid)*(ll)v1; if(co>temp) { co=temp; lret=st; rret=i; ret=v1; } } printf("%lld\n",co); for(int i=1;i<=n;i++) { if(i<lret||i>rret) { printf("%d\n",a[i]); }else { printf("%d\n",ret); } } return 0; }

bzoj 1112 poi 2008 磚塊