★实验任务
众所周知,dota 中神谕者的大招可以抵挡所有伤害,但是当大招结束时会一次性结算所有伤害。神谕者在大招期间已经遭受了 n 次伤害,他现在希望知道自己所遭受伤害中的第 k 小伤害值,但是他在思考时会再次遭受到伤害,他已经没办法准确的得知答案。所以他现在来请求你的帮助。
★数据输入
输入第一行包括两个整数 n,m(1<=n,m<=50000)。第二行有 n 个整数,为神谕者已遭受到的伤害。接下来有 m 次询问,每次询问时有两个整数 a,k;当 a 为 0 时意味着神谕者再次受到伤害,则 k 为其所受伤害值;当 a 为 1 是意味着神谕者想知道他所受伤害中的第 k(k 不会超过已受到的伤害次数)小伤害。
★数据输出
众所周知,dota 中神谕者的大招可以抵挡所有伤害,但是当大招结束时会一次性结算所有伤害。神谕者在大招期间已经遭受了 n 次伤害,他现在希望知道自己所遭受伤害中的第 k 小伤害值,但是他在思考时会再次遭受到伤害,他已经没办法准确的得知答案。所以他现在来请求你的帮助。
★数据输入
输入第一行包括两个整数 n,m(1<=n,m<=50000)。第二行有 n 个整数,为神谕者已遭受到的伤害。接下来有 m 次询问,每次询问时有两个整数 a,k;当 a 为 0 时意味着神谕者再次受到伤害,则 k 为其所受伤害值;当 a 为 1 是意味着神谕者想知道他所受伤害中的第 k(k 不会超过已受到的伤害次数)小伤害。
★数据输出
当 a 为 1 是输出第 k 小伤害值。
输入示例
5 5
1 3 5 7 9
1 4
0 6
1 4
0 5
1 4
输出示例
7
6
5
我以为插入的时候k也是50000以内,那就直接fenwick树解决,后来一问,范围好像是Int,那就只好用划分树或者平衡树了。
听同学说插入排序竟然过了。。。我刚也敲了一个,还真。。。。这数据得多水?
方法一:插入排序
一开始先用nlogn排序一次。O(nlogn)
对于每次插入的数进行插入,最坏情况下遍历整个数组(从最后到最开始)O(m*n)
对于每次插入的数进行插入,最坏情况下遍历整个数组(从最后到最开始)O(m*n)
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=100000+10;
int a[MAXN];
int main()
{
int n,m,i;
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
scanf("%d",&a[i]);
sort(a,a+n);
int cmd,k,len=n;
for(i=0;i<m;i++)
{
scanf("%d%d",&cmd,&k);
if(cmd==1)
printf("%d\n",a[k-1]);
else
{
//插入排序
int j;
if(len==0)
{
a[0]=k;
len++;
continue;
}
for(j=len;j>=0;j--)
{
if(k < a[j-1])
a[j]=a[j-1];
else
{
a[j]=k;
break;
}
}
len++;
}
}
}
方法二:离线+划分树
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXM=20;
const int MAXN=100000+10;
int data[MAXM][MAXN], num[MAXM][MAXN], sorted[MAXN];
struct node
{
int kind; //kind=0 代表插入 kind=1则为查询
int k;
}p[MAXN];
void Build(int depth, int L, int R) {
if (L == R)
return;
int same, mid, i, left, right;
mid = (L + R) >> 1;
same = mid - L + 1;
left = L;
right = mid + 1;
for (i = L; i <= R; i++) {
if (data[depth][i] < sorted[mid])
same--;
}
//same用来标记和中间值val_mid 相等的,且分到左孩子的数的个数。
for (i = L; i <= R; i++) {
if (data[depth][i] < sorted[mid])
data[depth + 1][left++] = data[depth][i];
else if (data[depth][i] == sorted[mid] && same) {
data[depth + 1][left++] = data[depth][i];
same--;
} else
data[depth + 1][right++] = data[depth][i];
num[depth][i] = num[depth][L - 1] + left - L;
//num记录元素所在区间的当前位置之前进入左孩子的个数
}
Build(depth + 1, L, mid);
Build(depth + 1, mid + 1, R);
}
int findk(int L, int R, int x, int y, int k,int depth) {
if (L == R)
return data[depth][L];
int mid, left, temp;
mid = (L + R) >> 1;
left = num[depth][y] - num[depth][x - 1];
temp = num[depth][x - 1] - num[depth][L - 1];
if (left >= k)
return findk( L, mid, L + temp, L + temp + left - 1, k,depth + 1);
else {
k -= left;
temp = x - L - temp;
left = y - x + 1 - left;
return findk( mid + 1, R, mid + temp + 1, mid + temp + left, k,depth + 1);
}
}
int main() {
int n,m,i;
int len=0;
scanf("%d%d", &n,&m);
for(i=0;i<n;i++)
{
scanf("%d",&p[i].k);
len++;
sorted[len] = data[0][len] = p[i].k;
p[i].kind=0; //insert
}
int tot=n+m;
for(;i<tot;i++)
{
int action;
scanf("%d%d",&action,&p[i].k);
if(action==0)
{
p[i].kind=0;
len++;
sorted[len] = data[0][len] = p[i].k;
}
else if(action==1)
{
p[i].kind=1;
}
}
sort(sorted + 1, sorted + len + 1);
Build(0, 1, len);
int cnt=0; //统计当前元素个数
for(i=0;i<tot;i++)
{
if(p[i].kind==0)
cnt++;
else
printf("%d\n",findk(1,len,1,cnt,p[i].k,0));
}
return 0;
}
方法三 带附加信息的BST
记录它的左儿子个数,方便查询。
查询的时候大于左儿子个数向右查询 k=k-num-1,否则向左
#include<cstdio>
struct node
{
node *left;
node *right;
int num;
int cntL;
node(){ left=right=NULL; cntL=0; }
};
struct BST
{
node *root;
BST() {root=NULL;}
void insert(int num)
{
node *p=root,*p_fa=NULL;
node *temp=new node;
temp->num=num;
while(p)
{
p_fa=p;
if(num <= p->num) //left;
{
p->cntL++;
p=p->left;
}
else //right
p=p->right;
}
if(root==NULL)
{
root=temp;
return;
}
if(num <= p_fa->num)
p_fa->left=temp;
else
p_fa->right=temp;
}
int find(int k)
{
node *p=root;
while(k!=p->cntL+1)
{
if(k> p->cntL) //大于就往右边查找
{
k=k-p->cntL-1;
p=p->right;
}
else
{
p=p->left;
}
}
return p->num;
}
}bst;
int main()
{
int n,m,i;
scanf("%d%d",&n,&m);
int cmd,k;
for(i=0;i<n;i++)
{
scanf("%d",&k);
bst.insert(k);
}
for(i=0;i<m;i++)
{
scanf("%d%d",&cmd,&k);
if(cmd==0)
bst.insert(k);
else
printf("%d\n",bst.find(k));
}
return 0;
}
方法四:SBT
我们都知道,BST在极端情况下会退化成一条链,那么查询效率将会变成O(n)
当然此题数据特别水,插入排序都过了,就不用说BST了。
但是真实情况是我们不知道水不水!
平衡树有AVL、红黑树、treap、SBT
也可以用splay,但是比较慢QAQ
AVL和红黑树写起来比较复杂,推荐treap和SBT
根据创造SBT的作者吹嘘,SBT是最快的,下面介绍SBT
当然此题数据特别水,插入排序都过了,就不用说BST了。
但是真实情况是我们不知道水不水!
平衡树有AVL、红黑树、treap、SBT
也可以用splay,但是比较慢QAQ
AVL和红黑树写起来比较复杂,推荐treap和SBT
根据创造SBT的作者吹嘘,SBT是最快的,下面介绍SBT
SBT全称叫Size Balanced Tree,也是一种平衡树。
他既不SB也不BT。
关于SBT树我的介绍:Size Balanced Tree(SBT树)整理http://blog.csdn.net/murmured/article/details/17029131
这一次好多人都用这个。。。
#include<cstdio>
const int MAXN=200000+10;
struct SBT
{
int left[MAXN]; //left son
int right[MAXN]; //right son
int size[MAXN]; //the num of sons
int value[MAXN]; //value
int len; //length
int root;
SBT(){ root=len=0; }
void right_rotate(int &t)
{
int k=left[t];
left[t]=right[k];
right[k]=t;
size[k]=size[t];
size[t]=size[ left[t] ] + size[ right[t] ] +1;
t=k;
}
void left_rotate(int &t)
{
int k=right[t];
right[t]=left[k];
left[k]=t;
size[k]=size[t];
size[t]=size[left[t]]+size[right[t]]+1;
t=k;
}
void insert(int &t,int v)
{
if(!t)
{
t=++len;
value[t]=v;
size[t]=1;
left[t]=right[t]=0;
return;
}
size[t]++;
if(v < value[t])
insert(left[t],v);
else
insert(right[t],v);
matain(t);
}
void matain(int &t)
{
if(size[ left[ left[t] ] ] > size[ right[t] ] )
{
right_rotate(t);
matain(right[t]);
matain(t);
}
else if( size[ right[ left[t] ] ]>size[ right[t] ] )
{
left_rotate(left[t]);
right_rotate(t);
matain(left[t]);
matain(right[t]);
matain(t);
}
else if(size[ right[ right[t] ] ]>size[ left[t] ])
{
left_rotate(t);
matain(left[t]);
matain(t);
}
else if(size[ left[ right[t] ] ]>size[ left[t] ])
{
right_rotate(right[t]);
left_rotate(t);
matain(left[t]);
matain(right[t]);
matain(t);
}
}
int select(int t,int k)
{
if(k==size[left[t]]+1)
return value[t];
if(k<=size[left[t]])
return select(left[t],k);
else
return select(right[t],k-size[left[t]]-1);
}
}sbt;
int main()
{
int n,m,i;
scanf("%d%d",&n,&m);
int cmd,k;
for(i=0;i<n;i++)
{
scanf("%d",&k);
sbt.insert(sbt.root,k);
}
for(i=0;i<m;i++)
{
scanf("%d%d",&cmd,&k);
if(cmd==0)
sbt.insert(sbt.root,k);
else
printf("%d\n",sbt.select(sbt.root,k));
}
return 0;
}