Bzoj1112:[POI2008]砖块Klo:splay

4 篇文章 0 订阅

题目链接:1112:[POI2008]砖块Klo

原题目是要输出最终高度的,代码中有体现

显然一段区间都变成某个数且代价最小,那么就变成中位数

所以我们维护中位数即可,上splay

一不小心把树建反成左大右小了QAQ

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1000010;
int n,m,mn,pos,root=0,cnt=0;
LL ans=1e16,a[maxn],ans2;

struct Nodes{
	int c[2],fa,size;
	LL s,val;
};

struct splay_tree{
	Nodes t[maxn]; 
	void push_up(int x){
		t[x].size=t[t[x].c[0]].size+t[t[x].c[1]].size+1;
		t[x].s=t[t[x].c[0]].s+t[t[x].c[1]].s+t[x].val;
	}
	void rotate(int p,int x){
        int mark= p==t[x].c[1];
        int y=t[p].c[mark^1],z=t[x].fa;
        if (t[z].c[0]==x) t[z].c[0]=p;
        if (t[z].c[1]==x) t[z].c[1]=p;
        if (y) t[y].fa=x;t[x].c[mark]=y;
        t[x].fa=p; t[p].fa=z; t[p].c[mark^1]=x;
        push_up(x);
    }
    void splay(int p,int k){
        while (t[p].fa!=k){
            int x=t[p].fa,y=t[x].fa;
            if (y==k) rotate(p,x);
            else if (p==t[x].c[0]^x==t[y].c[0]) rotate(p,x),rotate(p,y);
            else rotate(x,y),rotate(p,x);
        } push_up(p);
        if (!k) root=p; return;
    }
    void new_splay(int v,int f){
		t[++cnt].fa=f; t[cnt].val=v;
		t[cnt].size=1; t[cnt].s=v;
		if (f) t[f].c[t[f].val>v]=cnt;
    }
    void ins(int p,LL v){
		if(!p){new_splay(v,0),splay(cnt,0);return;}
		int fat=p;
		while (p){
			fat=p;
			if (t[p].val>v) p=t[p].c[1];
			else p=t[p].c[0];
		}
		new_splay(v,fat);
		splay(cnt,0);
    }
    void del(int p){
		splay(p,0); int tmp=t[p].c[0];
		t[t[p].c[0]].fa=0; t[t[p].c[1]].fa=0;
		if (!t[p].c[1]){root=t[p].c[0];return;}
		if (!t[p].c[0]){root=t[p].c[1];return;}
		while (t[tmp].c[1]) tmp=t[tmp].c[1];
		splay(tmp,0);
		t[t[p].c[1]].fa=tmp;
		t[tmp].c[1]=t[p].c[1];
		push_up(tmp);
    }
    int findkth(int p,int k){
		if (t[t[p].c[0]].size+1==k) return p;
		if (t[t[p].c[0]].size+1<k)
		    return findkth(t[p].c[1],k-t[t[p].c[0]].size-1);
		else return findkth(t[p].c[0],k);
    }
    void getans(int i){
		splay(findkth(root,mn),0);
		LL mid=t[root].val; int tmp=root;
		LL ret=mid*t[t[tmp].c[0]].size-t[t[tmp].c[0]].s;
		ret=ret+t[t[tmp].c[1]].s-mid*t[t[tmp].c[1]].size;
		ret=-ret;
		if (ret<ans){ans=ret;pos=i;ans2=mid;}
    }
}s;

int main(){
	scanf("%d%d",&n,&m); mn=(m+1)/2;
	for (int i=1;i<=n;++i) scanf("%lld",&a[i]);
	for (int i=1;i<=m;++i) s.ins(root,a[i]);
	s.getans(1);
	for (int i=m+1;i<=n;++i){
		s.del(i-m); s.ins(root,a[i]);
		s.getans(i-m+1);
	}
	printf("%lld\n",ans);
	for (int i=pos;i<=pos+m-1;++i) a[i]=ans2;
	for (int i=1;i<=n;++i) printf("%lld\n",a[i]);
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值