#170. 气球

把气球建成一颗线段树,线段树上第i个节点表示当前状态下个数为i的气球有几种。

先把小朋友从小到大sort。

然后每次从线段树上取出最大的a[i]种气球给第i个小朋友,然后这a[i]个气球在线段树上的位置都向左移一位。

把节点下标建成链表,设这些气球中个数最小值为x,x节点权值为y,取出了k种,则只需在链表和线段树中把x这个节点删去,然后在zuo[x]上加k,在you[x]上加y-k。注意当zuo[x]不存在时不作处理,you[x]不存在时不能把x删去,而是要把x的值修改为y-k

由于这题数据较大(n<=1000000),可以把线段树改为树状数组。

注意树状数组上求第k小值时要判x<=n!简直有毒!就因为这个把树状数组上二分写萎了!!我作业还有一大坨!!

丑陋的代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define ll long long
#define pli pair<ll,int>
#define mkp make_pair
#define X first
#define Y second
#define N 2000005
ll n,m,a[N],T[N],nn,zuo[N],you[N];
void add(ll x,ll y){
	for(ll z=x;z<=n;z+=z&-x)T[z]+=y;
}
ll query(ll x){
	ll y=0;
	for(;x;x-=x&-x)y+=T[x];
	return y;
}
ll kth(ll k){
	ll x=0;
	for(ll z=nn;z;z>>=1){
		if(T[x|z]<=k&&(x|z)<=n)k-=T[x+=z];
	}
	return x+1;
}
int main(){
	ll i,x,y,z,k;
	scanf("%lld%lld",&n,&m);
	for(nn=1;nn<=n;nn<<=1);nn>>=1;
	rep(i,1,n)scanf("%lld",&a[i]);
	rep(i,2,n)zuo[i]=i-1,you[i-1]=i;
	sort(a+1,a+n+1);
	while(m--){
		scanf("%lld",&x);
		if(x)add(min(x,n),1);
	}
	rep(i,1,n)if(a[i]){
		k=query(n)-a[i];
		if(k<0)break;
		x=kth(k);
 		y=query(x);z=query(x-1);
		k-=z;y-=z;add(x,-y);
		if(you[x])add(you[x],k),zuo[you[x]]=zuo[x];
		else add(x,k);
		if(zuo[x])add(zuo[x],y-k),you[zuo[x]]=you[x];
	}
	printf("%lld\n",i-1);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值