1112: [POI2008]砖块Klo

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 2910 Solved: 1026
[Submit][Status][Discuss]

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

Input
第一行给出N,K. (1 ≤ k ≤ n ≤ 100000), 下面N行,每行代表这柱砖的高度.0 ≤ hi ≤ 1000000

Output
最小的动作次数

Sample Input
5 3

3

9

2

3

1
Sample Output
2


不难想到,对于一个区间,我们要找到把不同数字变为相同的最小步数,就是要找到中位数,所以我们就要去维护中位数。
诶?中位数不就是第k大的问题吗?于是我们可以用权值线段树去维护。

然后权值线段树维护区间个数,和权值和即可。

这道题用平衡树会快很多,权值线段树不离散化会TLE(或者我写得菜)。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int n,k,m,a[N],res=1e18,vis[N*10],b[N],cnt;
struct node{
	int l,r,sum,num;
}t[N<<2];
inline void push_up(int p){
	t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
	t[p].num=t[p<<1].num+t[p<<1|1].num;
}
void build(int p,int l,int r){
	t[p].l=l; t[p].r=r;
	if(l==r)	return ; int mid=l+r>>1;
	build(p<<1,l,mid); build(p<<1|1,mid+1,r);
}
void change(int p,int x,int v){
	if(t[p].l==t[p].r){
		t[p].sum+=b[x]*v,t[p].num+=v;	return ;
	}
	int mid=t[p].l+t[p].r>>1;	
	if(x<=mid)	change(p<<1,x,v);
	else	change(p<<1|1,x,v);
	push_up(p);
}
int ask(int p,int l,int r,bool flag){
	if(t[p].l==l&&t[p].r==r)	return (flag?t[p].num:t[p].sum);
	int mid=t[p].l+t[p].r>>1;
	if(r<=mid)	return ask(p<<1,l,r,flag);
	else if(l>mid)	return ask(p<<1|1,l,r,flag);
	else	return ask(p<<1,l,mid,flag)+ask(p<<1|1,mid+1,r,flag);
}
int kth(int p,int k){
	if(t[p].l==t[p].r)	return t[p].l;
	int mid=t[p].l+t[p].r>>1;	int ls=t[p<<1].num;
	if(k<=ls)	return kth(p<<1,k);
	else	return kth(p<<1|1,k-ls);
}
signed main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++)	scanf("%lld",&a[i]),b[i]=a[i];
	sort(b+1,b+1+n); m=unique(b+1,b+1+n)-b-1;
	for(int i=1;i<=m;i++)	vis[b[i]]=++cnt;
	build(1,1,m);
	for(int i=1;i<=k;i++)	change(1,vis[a[i]],1);
	for(int i=k;i<=n;i++){
		int mid=kth(1,(k+1)/2),ln=0,rn=0,ls=0,rs=0;
		if(1<=mid-1)	ln=ask(1,1,mid-1,1);
		if(mid+1<=m)	rn=ask(1,mid+1,m,1);
		if(1<=mid-1) 	ls=ask(1,1,mid-1,0);
		if(mid+1<=m)	rs=ask(1,mid+1,m,0);
		res=min(res,rs-rn*b[mid]+ln*b[mid]-ls);
		change(1,vis[a[i-k+1]],-1); change(1,vis[a[i+1]],1);
	}
	printf("%lld\n",res);
	return 0;
}