数据结构与算法实验题 10.1 神谕者

来源:互联网 发布:淘宝一件代发货源网免费加盟代理 编辑:程序博客网 时间:2024/04/29 14:49
★实验任务 
众所周知,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

输出示例 



5


我以为插入的时候k也是50000以内,那就直接fenwick树解决,后来一问,范围好像是Int,那就只好用划分树或者平衡树了。

听同学说插入排序竟然过了。。。我刚也敲了一个,还真。。。。这数据得多水?

方法一:插入排序

一开始先用nlogn排序一次。O(nlogn)
对于每次插入的数进行插入,最坏情况下遍历整个数组(从最后到最开始)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--;} elsedata[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;elsep_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);elseprintf("%d\n",bst.find(k));}return 0;}


方法四:SBT

我们都知道,BST在极端情况下会退化成一条链,那么查询效率将会变成O(n)
当然此题数据特别水,插入排序都过了,就不用说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 sonint right[MAXN]; //right sonint size[MAXN];   //the num of sonsint value[MAXN];  //valueint len;//lengthint 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);elseinsert(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);elsereturn 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);elseprintf("%d\n",sbt.select(sbt.root,k));}return 0;}