[BZOJ1112]砖块klo Treap

诶算是一次想出来解法了

考虑枚举连续的k个元素,最后统一高度为x,ans = sigma( abs( xi - x ) )

用一点数学知识就能知道,当x为xi的中位数的时候答案最优

Treap维护枚举的连续k个元素(需要记录每个子树的和),求中位数(第k/2大)


#include <iostream>
#include <cstdio>
#include <cstdlib>
    
#define N 400050
#define INF 1000000000
    
using namespace std;
typedef long long LL;
LL sum[N],tr[N],tot,pre,ter,v,ans;
int ls[N],rs[N],siz[N],r[N],a[N],rt,cnt,n,k;
inline void ut(LL &x,LL y) { x = min(x,y); }
inline void update(int &t) {
    if (!t) return ;
    sum[t] = sum[ ls[t] ] + sum[ rs[t] ] + tr[t];
    siz[t] = siz[ ls[t] ] + siz[ rs[t] ] + 1;
}
    
inline void lturn(int &t) {
    int d = rs[t]; 
    rs[t] = ls[d];
    ls[d] = t;
    update(t); update(d);
    t = d;
}
    
inline void rturn(int &t) {
    int d = ls[t];
    ls[t] = rs[d];
    rs[d] = t;
    update(t); update(d);
    t = d;
}
   
void ins(int x,int &t) {
    if (!t) {
        tr[ t=++cnt ] = 1LL * x;
        sum[t] = 1LL * x;
        siz[t] = 1LL;
        r[t] = rand();
        return ;
    }
    sum[t] += x; siz[t]++;
    if (x <= tr[t]) {
        ins(x,ls[t]);
        if (r[ ls[t] ] > r[t]) rturn(t);
    } else {
        ins(x,rs[t]);
        if (r[ rs[t] ] > r[t]) lturn(t);
    }
}
   
void query(int k,int t) {
    if (!t) return ;
    if (k == siz[ ls[t] ] + 1) {
        pre += sum[ ls[t] ] + tr[t];
        v = tr[t];
        return ;
    }
    if (siz[ ls[t] ] >= k) 
        query(k,ls[t]);
    else
        pre += sum[ ls[t] ] + tr[t] , query(k-siz[ls[t]]-1,rs[t]);
}
   
void dec(int x,int &t) {
    if (!t) return ;
    if (x == tr[t]) {
        if (!(ls[t]*rs[t])) {
            int tt = t;
            t = ls[t] + rs[t];
            //update(t);
            tr[tt] = siz[tt] = sum[tt] = ls[tt] = rs[tt] = 0;
            return ; 
        }
        if (r[ ls[t] ] > r[ rs[t] ]) 
            rturn(t) , siz[t]-- , sum[t] -= x , dec(x,rs[t]); 
        else
            lturn(t) , siz[t]-- , sum[t] -= x , dec(x,ls[t]);
        return ;
    }
    siz[t]--; sum[t] -= x;
    if (x < tr[t]) dec(x,ls[t]); else dec(x,rs[t]);
}
    
void solve() {
    ans = (1LL << 62);
    for (int i=1;i<=k;i++) ins(a[i],rt) , tot += 1LL * a[i];
    for (int i=k+1;i<=n;i++) {
        pre = 0; query((k+1)/2 , rt);
        ter = tot - pre;
        LL p1 = (k+1)/2 , p2 = k - p1;
        ut(ans, p1*v - pre + ter - p2*v);
            
        ins(a[i],rt); 
        dec(a[i-k],rt);
        tot = tot - a[i-k] + a[i];
    }
    pre = 0; query((k+1)/2 , rt);
    ter = tot - pre;
    LL p1 = (k+1)/2 , p2 = k - p1;
    ut(ans, 1LL * p1 * v - pre + ter - p2 * v);
}
    
inline void init() {
    scanf("%d%d",&n,&k);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
}
    
int main() {
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    init();
    solve();
    #ifdef ONLINE_JUDGE
        printf("%lld\n",ans);
    #else
        printf("%I64d\n",ans);
    #endif
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值