#170. 气球

来源:互联网 发布:内蒙古夜间灯光数据 编辑:程序博客网 时间:2024/04/28 07:14

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


1 0