题意 输入0 a 把a压入容器 输入1 a 把a从容器中删除 输入 2 a b 输出大于a的第b个数
两个月前刚开始做题,啥也不会的时候,好像刚自学着搜索,跟着大佬们去上了一节树状数组,那时候居然没有一脸蒙蔽 真是佩服自己,而且后来居然还回去好好看了树状数组的博客,太佩服自己了,现在看着 觉得树状数组的操作比线段树要简单很多。但是如果要用线段树实现树状数组的功能却非常麻烦。树状数组的初始化,更新,求和,用来维护的数组储存的时候每一段区间的和 一个很神奇的东西 #define Lowbit ( (x)&(-x) ) 有了它你就能创建一棵平衡二叉树。每次都是合并同等高度的子树。
使得复杂度降得log(n)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define N 100010
#define Lowbit(x) ((x)&(-x))
int C[N];
bool vis[N];
void add(int x,int num) //加入元素num为1 删除元素 num为-1.
{
while(x<N)
{
C[x]+=num;
x+=Lowbit(x); //开辟整个树状数组的空间
}
}
int Sum(int x)
{
int ans=0;
while(x>0)
{
ans+=C[x];
x-=Lowbit(x); //利用树状数组的性质求和。每次都是移位的运算。
} //求的是区间和
return ans;
}
int binarysearch(int l,int k)
{
int mid,left,right,tmp;
left=l+1; //查找的范围是从 l+1开始的 第k个存在的数
right=N-1; //因为树状数组这种方法就是查找整个树状数组这个容器的所有数 所以 右边界是 N-1
if(Sum(right)-Sum(l)<k) return -1; //首先判断的是整个树状数组的全部区间的存在个数是否满足 第k个 如果不满足 直接返回-1
//由于是用数组的下标来存储元素 如果这个元素存在这个容器里面
while(left<=right) //那么vis 就为1 而且 C[x]++;C这个数组下标是元素 存的是元素的个数
{ //再利用二分查找 使得查找降为log n 简直太聪明了
mid=(left+right)>>1;
tmp=Sum(mid)-Sum(l);
if(tmp==k) //如果中点到左边界的里面存在数的个数刚刚好 那么就从中点作为右边界开始找就行。
{
if(vis[mid]) //如果二分的中点刚好是所求点 那么判断他是否存在就行
return mid; //如果不存在 因为 Sum(mid) 找的数肯定不会超过mid 把右边界更新为mid-1
//就行
right=mid-1; //这里的二分和我前天晚上做的那个codeforces 很像 虽然我自己做出来了,这里居然没认出来 好丢人呐。。
}
else if(tmp>k)
{ //如果里面存在的数比要找的多 那么也是首先判断中点是不是
if(Sum(mid-1)-Sum(l)<k) return mid; //相当于顺便判断了 vis[mid]==1
right = mid -1;
}
else{
left=mid+1; //如果里面存在的数比要找的少 那么肯定不在这个区间 所以把左边界更新为mid+1
}
}
return -1;
}
int main()
{
int p,n,m,T,e,a,k,l,r,mid;
while(~scanf("%d",&T))
{
memset(C,0,sizeof(C));
memset(vis,0,sizeof(vis));
while(T--)
{
scanf("%d",&p);
switch(p)
{
case 0:
scanf("%d",&e);
add(e,1); //初始化树状数组
vis[e]=1;
break;
case 1:
scanf("%d",&e);
m=Sum(e)-Sum(e-1);
if(!m) printf("No Elment!\n");
else add(e,-1);
if(m==1) vis[e]=0;
break;
case 2:
scanf("%d%d",&a,&k);
m=binarysearch(a,k);
if(m==-1)
printf("Not Find!\n");
else
printf("%d\n",m);
break;
}
}
}
}