一些数据结构,比如线段树或平衡树,他们一般是要么维护每个元素在原序列中的排列顺序,要么是维护每个元素的大小顺序,若是像二者兼得那么就想想主席树.
给你多个不同的数字问你1-N第K大的数是多少 最简单的方法是直接排序然后输出第K个
但是如果用线段树做的话该怎么做
我们可以先离散化这N个数 然后把线段树底部第i个节点的值设为第i大的值出现了多少次 然后pushup rt[i]=rt[i<<1]+rt[i<<1|1]
这样我们从线段树的根节点二分就可以知道答案了
主席树就是有N个节点 每个节点是一棵arr[i]插入后的线段树
因为修改一个底部节点只更改LOGN个节点 所以空间复杂度变得可以接受
当静态询问L-R区间第K大的数时 我们只要在第R个线段树减去第L-1个线段树的差线段树上二分寻找就可以了
动态询问的话 需要树套树 在外面再套个树状数组解决
代码解析
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #define MAXN 100010 using namespace std; int tol; //若tol值相同,则L、R、sum就表示同一个节点 //L为左端点的编号,R为右端点的编号,sum表示区间[L,R]内数的个数 int L[MAXN<<5],R[MAXN<<5],sum[MAXN<<5]; int a[MAXN],T[MAXN],Hash[MAXN]; //T记录每个元素对应的根节点 //建树函数,建立一颗空树 int build(int l,int r) { //参数表示左右端点 int mid,root=++tol; sum[root]=0; //区间内数的个数为0 if(l<r) { mid=(l+r)>>1; L[root]=build(l,mid); //构造左子树并将左端点编号存入L R[root]=build(mid+1,r); //构造右子树并将右端点编号存入R } return root; } //更新函数 int update(int pre,int l,int r,int pos) {//参数分别为:上一线段树的根节点编号,左右端点,插入数在原数组中排第pos //从根节点往下更新到叶子,新建立出一路更新的节点,这样就是一颗新树了。 int mid,root=++tol; L[root]=L[pre]; //先让其等于前面一颗树 R[root]=R[pre]; //先让其等于前面一颗树 sum[root]=sum[pre]+1; //当前节点一定被修改,数的个数+1 if(l<r) { mid=(l+r)>>1; if(pos<=mid) L[root]=update(L[pre],l,mid,pos); //插入到左子树 else R[root]=update(R[pre],mid+1,r,pos); //插入到右子树 } return root; } //查询函数,返回的是第k大的数在原数组中排第几 int query(int u,int v,int l,int r,int k) { //参数分别为:两颗线段树根节点的编号,左右端点,第k大 //只会查询到相关的节点 int mid,num; if(l>=r) return l; mid=(l+r)>>1; num=sum[L[v]]-sum[L[u]]; //当前询问的区间中左子树中的元素个数 //如果左儿子中的个数大于k,则要查询的值在左子树中 if(num>=k) return query(L[u],L[v],l,mid,k); //否则在右子树中 else return query(R[u],R[v],mid+1,r,k-num); } int main() { int i,n,m,t,d,pos; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%d",&a[i]); Hash[i]=a[i]; } sort(Hash+1,Hash+n+1); d=unique(Hash+1,Hash+n+1)-Hash-1; //d为不同数的个数 tol=0; //编号初始化 T[0]=build(1,d); //1~d即区间 for(i=1;i<=n;i++) { //实际上是对每个元素建立了一颗线段树,保存其根节点 pos=lower_bound(Hash+1,Hash+d+1,a[i])-Hash; //pos就是当前数在原数组中排第pos T[i]=update(T[i-1],1,d,pos); } int l,r,k; while(m--) { scanf("%d%d%d",&l,&r,&k); pos=query(T[l-1],T[r],1,d,k); printf("%d ",Hash[pos]); } } return 0; }
POJ 2104 结构体版
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #define MAXN 100010 using namespace std; struct ZXS { int l, r; int sum; } Tree[MAXN << 5]; int tol; int num[MAXN], T[MAXN], idx[MAXN]; int build(int l, int r) { int mid, root = ++tol; Tree[root].sum = 0; if (l < r) { mid = (l + r) >> 1; Tree[root].l = build(l, mid); Tree[root].r = build(mid + 1, r); } return root; } int update(int pre, int l, int r, int pos) { int mid, root = ++tol; Tree[root].l = Tree[pre].l; Tree[root].r = Tree[pre].r; Tree[root].sum = Tree[pre].sum + 1; if (l < r) { mid = (l + r) >> 1; if (pos <= mid) { Tree[root].l = update(Tree[pre].l, l, mid, pos); } else { Tree[root].r = update(Tree[pre].r, mid + 1, r, pos); } } return root; } int query(int askl, int askr, int l, int r, int k) { int mid, num; if (l >= r) { return l; } mid = (l + r) >> 1; num = Tree[Tree[askr].l].sum - Tree[Tree[askl].l].sum; if (num >= k) { return query(Tree[askl].l, Tree[askr].l, l, mid, k); } else { return query(Tree[askl].r, Tree[askr].r, mid + 1, r, k - num); } } int main() { int n, m, t, len, pos; scanf("%d", &t); while (t--) { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", &num[i]); idx[i] = num[i]; } sort(idx + 1, idx + n + 1); len = unique(idx + 1, idx + n + 1) - idx - 1; tol = 0; T[0] = build(1, len); for (int i = 1; i <= n; i++) { pos = lower_bound(idx + 1, idx + len + 1, num[i]) - idx; T[i] = update(T[i - 1], 1, len, pos); } int l, r, k; while (m--) { scanf("%d%d%d", &l, &r, &k); pos = query(T[l - 1], T[r], 1, len, k); printf("%d ", idx[pos]); } } return 0; }
给一个长为N的数列,有M次操作,每次操作是以下两种之一: (1)修改数列中的一个数 (2)求数列中某位置在某次操作后的值
输入 第一行两个正整数N和M。 第二行N个整数表示这个数列。
接下来M行,每行开头是一个字符,若该字符为’M’,则表示一个修改操作,接下来两个整数x和y,表示把x位置的值修改为y;若该字符为’Q’,则表示一个询问操作,接 下来两个整数x和y,表示求x位置在第y次操作后的值。
输出 对每一个询问操作单独输出一行,表示答案。
样例输入
5 3
1 2 3 4 5
Q 1 0
M 1 3
Q 1 2
样例输出
1
3
提示
1<=N<=10^5,1<=M<=10^5
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #define MAXN 100010 using namespace std; struct ZXS { int l, r; int value; } Tree[MAXN << 5]; int tol, cur; int num[MAXN], T[MAXN], idx[MAXN]; int build(int l, int r) { int mid, root = ++tol; if (l == r) { Tree[root].value = num[l]; } else if (l < r) { mid = (l + r) >> 1; Tree[root].l = build(l, mid); Tree[root].r = build(mid + 1, r); } return root; } int update(int pre, int l, int r, int pos, int x) { int mid, root = ++tol; Tree[root].l = Tree[pre].l; Tree[root].r = Tree[pre].r; if (l == r) { Tree[root].value = x; } else if (l < r) { mid = (l + r) >> 1; if (pos <= mid) { Tree[root].l = update(Tree[pre].l, l, mid, pos, x); } else { Tree[root].r = update(Tree[pre].r, mid + 1, r, pos, x); } } return root; } int query(int ask, int l, int r, int pos) { int mid, num; if (l >= r) { return Tree[ask].value; } mid = (l + r) >> 1; if (pos <= mid) { return query(Tree[ask].l, l, mid, pos); } else { return query(Tree[ask].r, mid + 1, r, pos); } } int main() { int n, m, pos, x; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", &num[i]); } tol = 0; T[0] = build(1, n); char ch; while (m--) { cin >> ch >> pos >> x; if (ch == 'Q') { cur++; T[cur] = T[cur - 1]; cout << query(T[x], 1, n, pos) << endl; } else if (ch == 'M') { cur++; T[cur] = update(T[cur - 1], 1, n, pos, x); } } return 0; }
描述 给一个空数列,有M次操作,每次操作是以下三种之一: (1)在数列后加一个数 (2)求数列中某位置的值
撤销掉最后进行的若干次操作(1和3)
输入 第一行一个正整数M。
接下来M行,每行开头是一个字符,若该字符为’A’,则表示一个加数操作,接下来一个整数x,表示在数列后加一个整数x;若该字符为’Q’,则表示一个询问操作,接下来一个整数x,表示求x位置的值;若该字符为’U’,则表示一个撤销操作,接下来一个整数x,表示撤销掉最后进行的若干次操作。
输出 对每一个询问操作单独输出一行,表示答案。
样例输入
9
A 1
A 2
A 3
Q 3
U 1
A 4
Q 3
U 2
Q 3
样例输出
3
4
3
提示 1<=M<=10^5
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #define MAXN 100010 using namespace std; struct ZXS { int l, r; int value; } Tree[MAXN << 5]; int tol, cur; int num[MAXN], T[MAXN], idx[MAXN]; int number[MAXN]; int build(int l, int r) { int mid, root = ++tol; if (l == r) { Tree[root].value = 0; } else if (l < r) { mid = (l + r) >> 1; Tree[root].l = build(l, mid); Tree[root].r = build(mid + 1, r); } return root; } int update(int pre, int l, int r, int pos, int x) { int mid, root = ++tol; Tree[root].l = Tree[pre].l; Tree[root].r = Tree[pre].r; if (l == r) { Tree[root].value = x; } else if (l < r) { mid = (l + r) >> 1; if (pos <= mid) { Tree[root].l = update(Tree[pre].l, l, mid, pos, x); } else { Tree[root].r = update(Tree[pre].r, mid + 1, r, pos, x); } } return root; } int query(int ask, int l, int r, int pos) { int mid, num; if (l >= r) { return Tree[ask].value; } mid = (l + r) >> 1; if (pos <= mid) { return query(Tree[ask].l, l, mid, pos); } else { return query(Tree[ask].r, mid + 1, r, pos); } } int main() { int n, pos, x; scanf("%d", &n); tol = 0; T[0] = build(1, 100000); number[0] = 0; char ch; while (n--) { cin >> ch >> x; if (ch == 'Q') { cout << query(T[cur], 1, 100000, x) << endl; } else if (ch == 'A') { cur++; T[cur] = update(T[cur - 1], 1, 100000, number[cur - 1] + 1, x); number[cur] = number[cur - 1] + 1; } else if (ch == 'U') { cur++; T[cur] = T[cur - 1 - x]; number[cur] = number[cur - 1 - x]; } } return 0; }