数据结构与算法实验题 10.1 神谕者
★实验任务
众所周知,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; }
新 blog : www.hrwhisper.me