P3466 [POI2008]KLO-Building blocks(Splay)

喜夏-厌秋 提交于 2020-02-05 09:40:43

题意:

N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:从某柱砖的顶端拿一块砖出来,丢掉不要了. 2:从仓库中拿出一块砖,放到另一柱.仓库无限大. 现在希望用最小次数的动作完成任务.你还要求输出结束状态时,每柱砖的高度


题解:
很显然,要让我们将这K个柱子变成一样高并且操作次数最少,就是求这K个数的中位数

所以我们需要用一种数据结构能够实现插入,删除,求第k大,它前面有多少个数,后面有多少个数。

然后根据这K个数和中位数就能计算出最少次数了

ans=min(i=lraix)ans = min(\sum_{i=l}^{r}|a_i-x|)

找到中位数后就可以知道

ans=min(i=lmidxai+i=mid+1raix)ans = min(\sum_{i=l}^{mid}x-a_i+\sum_{i=mid+1}^{r}a_i-x)

左边可以通过将中位数伸展到根节点,然后求出比该数小的和及比它大的数的和即可

szlefta[mid]t[l].sum+t[r].sumszrighta[mid]sz_{left}*a[mid] - t[l].sum + t[r].sum - sz_{right}*a[mid]

这里我用的是Splay,当然fhq,zkw线段树,主席树都行。有思路就好做了,接下来就是码农时间了。


AC代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long LL;
const int MAXN = 1e5+10;
const int MOD = 998244353;
struct node{ int son[2],fa,cnt,sz,sum,val; }t[MAXN];
int root,tot,a[MAXN];
inline int id(int x){ return x==t[t[x].fa].son[1]; }
inline void pushup(int x){
    t[x].sz = t[t[x].son[0]].sz+t[t[x].son[1]].sz+t[x].cnt;
    t[x].sum = t[t[x].son[0]].sum + t[t[x].son[1]].sum+t[x].val*t[x].cnt;
}
inline void rotate(int x){
    int y=t[x].fa,z=t[y].fa,k=id(x);
    t[z].son[id(y)]=x; t[x].fa=z;
    t[y].son[k]=t[x].son[k^1]; t[t[x].son[k^1]].fa=y;
    t[x].son[k^1]=y; t[y].fa=x;
    pushup(y); pushup(x);
}
inline void splay(int x,int pos){
    while(t[x].fa!=pos){
        int y=t[x].fa,z=t[y].fa;
        if(z!=pos) id(x)==id(y) ? rotate(y):rotate(x);
        rotate(x);
    }
    if(!pos) root=x;
}
inline void update(int u,int fa,int x){
    t[u].cnt=t[u].sz=1;
    t[u].fa=fa; t[u].sum=t[u].val=x;
}
inline void Insert(int x){
    if(!root){
        root=++tot;
        update(root,0,x); return;
    }
    int u=root;
    while(1){
        t[u].sum += x; t[u].sz ++;
        if(t[u].val==x) { t[u].cnt++; splay(u,0); return; }
        int v = x>t[u].val;
        if(!t[u].son[v]){
            t[u].son[v]=++tot; update(t[u].son[v],u,x);
            u=t[u].son[v]; splay(u,0); return;
        }
        u=t[u].son[v];
    }
}
inline void del(int x){
    int u=root;
    while(t[u].val!=x) u=t[u].son[x>t[u].val];
    splay(u,0);
    t[u].cnt--; t[u].sz--; t[u].sum-=x;
    if(t[u].cnt) return;
    int nxt=t[u].son[1];
    while(nxt && t[nxt].son[0]) nxt=t[nxt].son[0];
    if(!nxt) { root=t[u].son[0]; t[root].fa=0; }
    else{
        splay(nxt,u);
        t[nxt].son[0]=t[u].son[0];
        t[t[u].son[0]].fa=nxt;
        t[root=nxt].fa=0;
        pushup(root);
    }
}
inline int kth(int x){
    int u=root;
    while(1){
        if(t[t[u].son[0]].sz>=x) u=t[u].son[0];
        else{
            if(t[t[u].son[0]].sz+1<=x && x<=t[t[u].son[0]].sz+t[u].cnt) return u;
            x -= t[t[u].son[0]].sz+t[u].cnt;
            u = t[u].son[1];
        }
    }
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
#endif // ONLINE_JUDGE
    int n,k; scanf("%lld%lld",&n,&k);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=1;i<=k;i++) Insert(a[i]);
    int mid = (k+1)/2,ans = 1e18,l=0,r=0,tag=0;
    for(int i=k;i<=n;i++){
        int u=kth(mid);
        splay(u,0);
        int res = 0;
        res = t[t[u].son[0]].sz*t[u].val-t[t[u].son[0]].sum+t[t[u].son[1]].sum-t[t[u].son[1]].sz*t[u].val;
        if(res<ans){ ans=res; l=i-k+1; r=i; tag=t[u].val; }
        del(a[i-k+1]);
        if(i+1<=n) Insert(a[i+1]);
    }
    printf("%lld\n",ans);
    for(int i=1;i<=n;i++)
        if(i>=l && i<=r) printf("%lld\n",tag);
        else printf("%lld\n",a[i]);
    return 0;
}


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!