HDU 2852 (树状数组+无序第K小)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2852
题目大意:操作①:往盒子里放一个数。操作②:从盒子里扔掉一个数。操作③:查询盒子里大于a的第K小数。
解题思路:
由于模型是盒子,而不是序列,所以可以用树状数组的顺序维护+逆序数思想。
对应的树状数组Solution:
放一个数
$Add(val,1)$
类似维护逆序数的方法,对应位置上计数+1。
注意Add的while范围要写成$while(x<maxn)$
如果范围不是最大,那么会导致某些sum[x]不会被更新。
删一个数
判断:$getSum(val)-getSum(val-1)=0$
可以Hash处理,但是没有必要。如果没有val这个数,那么$getSum(val)=getSum(val-1)$是必然的。
删除:$Add(val,-1)$
即加上-1,撤销之前的操作。
查询
查询比较麻烦。
首先要判断$getSum(maxn-1)-getSum(val)>=k$
然后,将查询大于a的第K小数转化为大于1的第X+K小数。
其中$X=getSum(val)$。然后,对区间$[1,maxn]$进行二分。
二分处理手段比较特殊,主要是由于有重复的数,所以直接找出$\arg \min \limits_{mid} getSum(mid)=X+K$是不行的。
$getSum(mid)=X+K$有时候并不能二分找到。
解决方法是:
$\left\{\begin{matrix}
R=mid \quad (getSum(mid)<=X+K)\\
\\
L=mid \quad (other)
\end{matrix}\right.$
这样,如果没有二分到,会最近的最小R作为结果。
$ans=R$
代码
#include "cstdio" #include "map" #include "cstring" #include "algorithm" using namespace std; #define LL long long #define maxn 100005 LL sum[maxn]; int val,n,kth,cmd; int lowbit(int x) {return x&(-x);} LL getSum(int x) { LL ret=0; while(x>0) { ret+=sum[x]; x-=lowbit(x); } return ret; } void update(int x,int s) { while(x<maxn) { sum[x]+=s; x+=lowbit(x); } } LL query(int a,int k) { LL low=getSum(a),res; low+=k; int l=1,r=maxn-1,m; while(l<r-1) { m=l+(r-l)/2; res=getSum(m); if(res>=low) r=m; else l=m; } return r; } int main() { //freopen("in.txt","r",stdin); while(scanf("%d",&n)!=EOF) { memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++) { scanf("%d",&cmd); if(cmd==0) { scanf("%d",&val); update(val,1); } else if(cmd==1) { scanf("%d",&val); LL key=getSum(val)-getSum(val-1); if(key==0) printf("No Elment!\n"); else update(val,-1); } else { scanf("%d%d",&val,&kth); if(getSum(maxn-1)-getSum(val)<kth) printf("Not Find!\n"); else { LL ans=query(val,kth); printf("%I64d\n",ans); } } } } }