【BZOJ2006】【NOI2010】超级钢琴 Heap+主席树

题解:

先维护个前缀和sum。然后以i为结束的串就可以有几个j作为开始节点,值sum[i]-sum[j-1]。

我们取最优的j,把sum-sum加到堆里面。

这样就可以O(1)取出最优串值,

取出后,对于这个i,就可以有一个第二优的j为开始的串,我们再把这个串值加入到堆中。

而这个j的优劣因为只跟sum有关,所以不妨写一个主席树寻找静态区间第K大,取得这个j。


这样我们只需要取j次,维护j次,就可以得到最终答案。

数据很良心啊,1A了。。数据范围有代码中注释


代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 501000 // N的范围50W,|Ai|<= 1000
#define LOGN 20
#define ls son[x][0]
#define rs son[x][1]
#define sz size[x]
#define inf 0x3f3f3f3f
using namespace std;
int sum[N],crs[N],n,m,L,R,rank;
long long ans;
struct LSH
{
	int x,id;
	bool operator < (const LSH &a)const{return x<a.x;} 
}lsh[N];
struct Heap
{
	int val[N],id[N],cnt;
	void pushup(int x){while((x>>1)&&val[x]>val[x>>1])swap(val[x],val[x>>1]),swap(id[x],id[x>>1]),x>>=1;}
	void pushdown(int x)
	{
		int son;
		while((x<<1)<=cnt)
		{
			son=((x<<1|1)<=cnt&&val[x<<1]<val[x<<1|1])?(x<<1|1):(x<<1);
			if(val[x]<val[son])swap(val[x],val[son]),swap(id[x],id[son]),x=son;
			else break;
		}
	}
	void push(int w,int note)
	{
		++cnt;
		val[cnt]=w,id[cnt]=note;
		pushup(cnt);
	}
	int top(){return val[1];}
	int pop()
	{
		int ret=id[1];
		val[1]=val[cnt],id[1]=id[cnt];
		cnt--;
		pushdown(1);
		return ret;
	}
}Heap;
struct Functional_Segment_Tree
{
	int son[N*LOGN][2],size[N*LOGN];
	int cnt,pos[N];
	void pushup(int x){sz=size[ls]+size[rs];}
	int build(int last,int l,int r,int d)
	{
		int x=++cnt;
		sz=size[last];
		if(l==r){sz++;return x;}
		int mid=l+r>>1;
		if(d<=mid)ls=build(son[last][0],l,mid,d),rs=son[last][1];
		else rs=build(son[last][1],mid+1,r,d),ls=son[last][0];
		pushup(x);
		return x;
	}
	int query(int last,int now,int l,int r,int k)
	{
		if(size[now]-size[last]<k)return inf;
		if(l==r){return l;}
		int temp=size[son[now][0]]-size[son[last][0]],mid=l+r>>1;
		if(temp<k)return query(son[last][1],son[now][1],mid+1,r,k-temp);
		else return query(son[last][0],son[now][0],l,mid,k);
	}
	void func()
	{
		for(int i=0;i<=n;i++)pos[i+1]=build(pos[i],1,rank,sum[i]);
	}
}fst;
int state[N];
int main()
{
//	freopen("test.in","r",stdin);
	int i,j,k;
	int a,b;
	scanf("%d%d%d%d",&n,&m,&L,&R);
	for(i=1;i<=n;i++)scanf("%d",&lsh[i+1].x),lsh[i+1].x+=lsh[i].x,lsh[i+1].id=i;
	sort(lsh+1,lsh+n+2);
	sum[lsh[1].id]=rank=1,crs[1]=lsh[1].x;
	for(i=2;i<=n+1;i++)
	{
		if(lsh[i].x!=lsh[i-1].x)crs[++rank]=lsh[i].x;
		sum[lsh[i].id]=rank;
	}
	fst.func();
	for(i=1;i<=n;i++)
	{
		a=max(-1,i-R-1)+1;
		b=max(-1,i-L)+1;
		k=fst.query(fst.pos[a],fst.pos[b],1,rank,state[i]=1);
		if(k<inf)Heap.push(crs[sum[i]]-crs[k],i);
	}
	while(m--)
	{
		if(!Heap.cnt)
		{
			puts("****");
			return 0;
		}
		ans+=Heap.top();
		j=Heap.pop();
		a=max(-1,j-R-1)+1;
		b=max(-1,j-L)+1;
		k=fst.query(fst.pos[a],fst.pos[b],1,rank,++state[j]);
		if(k<inf)Heap.push(crs[sum[j]]-crs[k],j);
	}
	cout<<ans<<endl;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值