HDU 2852 (树状数组+无序第K小)
题目大意:操作①:往盒子里放一个数。操作②:从盒子里扔掉一个数。操作③:查询盒子里大于a的第K小数。
解题思路:
由于模型是盒子,而不是序列,所以可以用树状数组的顺序维护+逆序数思想。
放一个数
Add(val,1)Add(val,1)
类似维护逆序数的方法,对应位置上计数+1。
注意Add的while范围要写成while(x<maxn)while(x<maxn)
如果范围不是最大,那么会导致某些sum[x]不会被更新。
删一个数
判断:getSum(val)−getSum(val−1)=0getSum(val)−getSum(val−1)=0
可以Hash处理,但是没有必要。如果没有val这个数,那么getSum(val)=getSum(val−1)getSum(val)=getSum(val−1)是必然的。
删除:Add(val,−1)Add(val,−1)
即加上-1,撤销之前的操作。
查询
查询比较麻烦。
首先要判断getSum(maxn−1)−getSum(val)>=kgetSum(maxn−1)−getSum(val)>=k
然后,将查询大于a的第K小数转化为大于1的第X+K小数。
其中X=getSum(val)X=getSum(val)。然后,对区间[1,maxn][1,maxn]进行二分。
二分处理手段比较特殊,主要是由于有重复的数,所以直接找出argminmidgetSum(mid)=X+KargminmidgetSum(mid)=X+K是不行的。
getSum(mid)=X+KgetSum(mid)=X+K有时候并不能二分找到。
解决方法是:
R=mid(getSum(mid)<=X+K)
L=mid(other)
这样,如果没有二分到,会最近的最小R作为结果。
ans=R
AC代码:
#include<stdio.h> #include<string.h> #define lowbit(i) (i&(-i)) using namespace std; const int maxn = 1e5+1; int c[maxn]; inline void add(int i, int val) { while(i<=100000){ c[i] += val; i += lowbit(i); } } int sum(int i) { int ans = 0; while(i>0){ ans += c[i]; i -= lowbit(i); } return ans; } int vis[maxn]; int Bin_search(int L, int R, int key, int index) { int mid; while(L < R){ mid = L + ((R-L)>>1); if(sum(mid) - sum(index) < key) L = mid + 1; else R = mid; } return R; } int main(void) { int n; while(~scanf("%d", &n) && n){ memset(c, 0, sizeof(c)); memset(vis, 0, sizeof(vis)); int command; int tot = 0; int M = 0; for(int t=1; t<=n; t++){ scanf("%d", &command); if(command==0){ int temp; scanf("%d", &temp); if(temp>M) M = temp; vis[temp]++; add(temp, 1); tot++; } else if(command==1){ int temp; scanf("%d", &temp); if(vis[temp]){ tot--; add(temp, -1); vis[temp]--; }else{ puts("No Elment!"); } } else{ int a, b; int ans; scanf("%d%d", &a, &b); if(tot-sum(a) >= b){ int ans = Bin_search(a, M, b, a); printf("%d\n", ans); }else{ puts("Not Find!"); } } } } return 0; }
前面思路还可以,但到后面的查询操作就不能很快解决导致TLE,问题是出在查询无序第k小问题身上,以后在解决无序第K小问题的时候,可以考虑这种算法