把气球建成一颗线段树,线段树上第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;
}