bzoj 1112: [POI2008]砖块Klo treap

题意

给出n个数,每次操作可以让一个数+1或-1,求连续的m个数,使得他们变成相同的数的操作数最少。
n,m<=100000

分析

假设现在给出m个数,要想使得他们变成相同的数的操作数最少,显然要让他们都变成这个m个数的中位数。
知道了这个后我们就可以用一棵treap来暴力这个m个值,然后每次查找中位数即可。

第一次用c++写treap,感觉好虚啊,顺便去借鉴了一波黄学长的删除操作。话说黄学长的代码真心简洁明了啊%%%

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 100005
#define ll long long
using namespace std;

int n,m,a[N],size,tot,root;
ll sum,ans;
struct tree{int size,val,in,l,r,k;ll s;}t[N];

void updata(int x)
{
    t[x].size=t[t[x].l].size+t[t[x].r].size+t[x].in;
    t[x].s=t[t[x].l].s+t[t[x].r].s+(ll)t[x].in*t[x].val;
}

void rttl(int &x)
{
    int y=t[x].r;
    t[x].r=t[y].l;
    t[y].l=x;
    updata(x);
    updata(y);
    x=y;
}

void rttr(int &x)
{
    int y=t[x].l;
    t[x].l=t[y].r;
    t[y].r=x;
    updata(x);
    updata(y);
    x=y;
}

void ins(int &x,int val)
{
    if (!x)
    {
        x=++tot;
        t[x].val=t[x].s=val;t[x].size=t[x].in=1;t[x].k=rand();
        return;
    }
    t[x].s+=val;t[x].size++;
    if (t[x].val==val)
    {
        t[x].in++;
        return;
    }
    if (val<t[x].val)
    {
        ins(t[x].l,val);
        if (t[t[x].l].k<t[x].k) rttr(x);
    }else
    {
        ins(t[x].r,val);
        if (t[t[x].r].k<t[x].k) rttl(x);
    }
}

void del(int &x,int val)
{
    if (!x) return;
    if (t[x].val==val)
    {
        if (t[x].in>1)
        {
            t[x].in--;t[x].size--;t[x].s-=val;return;
        }
        else if (t[x].l*t[x].r==0)
        {
            x=t[x].l+t[x].r;return;
        }
        else if (t[t[x].l].k<t[t[x].r].k)
        {
            rttr(x);del(t[x].r,val);
        }
        else
        {
            rttl(x);del(t[x].l,val);
        }
    }
    else if (val<t[x].val) del(t[x].l,val);
    else del(t[x].r,val);
    updata(x);
}

int find(int x,int y)
{
    if (t[t[x].l].size+1<=y&&t[t[x].l].size+t[x].in>=y)
    {
        sum+=t[t[x].l].s;size+=t[t[x].l].size;
        return t[x].val;
    }
    if (t[t[x].l].size+t[x].in<y)
    {
        sum+=t[t[x].l].s+(ll)t[x].val*t[x].in;size+=t[t[x].l].size+t[x].in;
        y-=t[t[x].l].size+t[x].in;
        return find(t[x].r,y);
    }else
    {
        return find(t[x].l,y);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    t[0].k=1e7;
    for (int i=1;i<=m;i++)
        ins(root,a[i]);
    ll ans=1e17;
    sum=size=0;
    int x=find(root,(1+m)/2);
    ans=min(ans,(ll)x*size-sum+t[root].s-sum-(ll)x*(m-size));
    for (int i=m+1;i<=n;i++)
    {
        del(root,a[i-m]);
        ins(root,a[i]);
        sum=size=0;
        int x=find(root,(1+m)/2);
        ans=min(ans,(ll)x*size-sum+t[root].s-sum-(ll)x*(m-size));
    }
    printf("%lld",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值