【主席树入门 && 区间内第k小的数】POJ - 2104 K-th Number
2017-09-22 11:47
573 查看
Problem Description
输入n, m代表有n个元素,m个访问。接下来输入n个元素。输入m行访问,每行有三个元素l r k。问你下标l - r里面第k小的数是几?
思路:
学长讲堂blue学长很棒的给我们入了个门。自己也能手敲出来这题,也算理解这题了。主席树由n个线段树组成,每个线段树只有logn的节点,只保存存在该元素的节点。
代码:
感觉这玩意注释,不好表达,所以附一份学长的模板代码
输入n, m代表有n个元素,m个访问。接下来输入n个元素。输入m行访问,每行有三个元素l r k。问你下标l - r里面第k小的数是几?
思路:
学长讲堂blue学长很棒的给我们入了个门。自己也能手敲出来这题,也算理解这题了。主席树由n个线段树组成,每个线段树只有logn的节点,只保存存在该元素的节点。
代码:
#include<cstdio> #include<algorithm> using namespace std; struct node { int sum, l, r;//该区间有几个数,记录左孩子对应的tree[]下标,记录右孩子对应tree[]下标 }; #define nn 100005 #define MID int mid = (l + r)/2 node tree[nn * 40];//n * logn的节点数 × 40肯定够了。 int a[nn], root[nn], cnt;//a[]存输入元素,root[]存每个线段数的根对应tree[]里的下标,cnt用的结点数 int sca[nn], num;//离散化后的数组,大小 int Creat(int sum, int l, int r)//申请一个新的结点 { ++cnt; tree[cnt].l = l; tree[cnt].r = r; tree[cnt].sum = sum; return cnt; } void Insert(int &root, int pre, int l, int r, int pos) { //申请一个结点保存,在前一颗树对应结点基础上copy。sum + 1元素多了一个。 root = Creat(tree[pre].sum + 1, tree[pre].l, tree[pre].r); if(l == r) return; MID; if(pos <= mid)//左边递归 Insert(tree[root].l, tree[pre].l, l, mid, pos); else Insert(tree[root].r, tree[pre].r, mid + 1, r, pos); } int Query(int s, int e, int l, int r, int k)//查找s+1 - e中第k小的数 { if(l == r) return l;//叶子结点,找到了返回 //左孩子的sum差。 int sum = tree[tree[e].l].sum - tree[tree[s].l].sum; MID; int red; if(k <= sum)//和k判断,如果k<=sum代表 k在左孩子那边 red = Query(tree[s].l, tree[e].l, l, mid, k); else red = Query(tree[s].r, tree[e].r, mid + 1, r, k - sum); return red; } int main() { int n, m, i; while(~scanf("%d %d", &n, &m)) { cnt = 0; root[0] = 0; for(i = 1; i <= n; i++) { scanf("%d", &a[i]); sca[i] = a[i]; } sort(sca + 1, sca + n + 1); num = unique(sca + 1, sca + n + 1) - (sca + 1);//离散化 for(i = 1; i <= n; i++) { int x = lower_bound(sca + 1, sca + n + 1, a[i]) - sca;//a[i]离散化后的值 //插入一个元素,相当于在前一棵树的copy上,建一颗新的树 Insert(root[i], root[i - 1], 1, num, x); } int l, r, k; while(m--) { scanf("%d %d %d", &l, &r, &k); printf("%d\n", sca[Query(root[l - 1], root[r], 1, num, k)]); } } }
感觉这玩意注释,不好表达,所以附一份学长的模板代码
// 主席树区间第K大 模板 const int MAXN = 100001; // 本代码中所有数据均按从下标 1 开始存放 // 主席树中的线段树结点,sum 表示此区间内元素个数 struct node { int sum, l, r; } hjt[MAXN*40]; int sorted[MAXN], num; // sorted: 离散化后的数组 num: 离散化后的数组长度 int root[MAXN], cnt; // root: 主席树中用来保存每棵线段树树根的数组 cnt: 线段树结点数(用于数组方式动态开点) // 查找离散化之后的下标 int GetIdx(int v) { return lower_bound(sorted+1, sorted+1+num, v) - sorted; } // 初始化 void Init() { cnt = 0; root[0] = 0; } // 创建结点 inline int CreateNode(int sum, int l, int r) { int idx = ++cnt; hjt[idx].sum = sum; hjt[idx].l = l; hjt[idx].r = r; return idx; } // 新建一棵线段树,只沿更新路径新建出较上个版本有修改的结点 // 调用参数 // root: 插入后新生成的线段树的根结点,直接赋值到 root 中 // pre_rt: 上一棵线段树的根 // pos: 本次要插入的值 // l, r: 递归参数。默认填写 1, num void Insert(int &root, int pre_rt, int pos, int l, int r) { // 动态创建结点,直接根据上一个版本复制对应的节点,sum+1 root = CreateNode(hjt[pre_rt].sum+1, hjt[pre_rt].l, hjt[pre_rt].r); if(l == r) return; int m = (l+r) >> 1; if(pos <= m) Insert(hjt[root].l, hjt[pre_rt].l, pos, l, m); else Insert(hjt[root].r, hjt[pre_rt].r, pos, m+1, r); } // 适用于查询区间 [l, r] 中的第 k 小。通常需要自行变通 // 调用参数 // s, e: 要查询区间所需的两个线段树的根,如要查询区间 [l, r],则传入 root[l-1], root[r] // l, r: 递归参数。默认填写 1, num // k: 要查询区间第几小 int Query(int s, int e, int l, int r, int k) { if(l == r) return l; int m = (l+r) >> 1; int sum = hjt[hjt[e].l].sum - hjt[hjt[s].l].sum; // 计算左子树的元素数量 if(k <= sum) // 如果 k <= sum,则 k 在左子树,否则在右子树 return Query(hjt[s].l, hjt[e].l, l, m, k); else return Query(hjt[s].r, hjt[e].r, m+1, r, k-sum); }
相关文章推荐
- POJ 2104 & HDU 2665 & POJ 2761 K-th Number (主席树入门题 区间第K大)
- poj 2104 K-th Number (静态区间第k大,主席树)
- POJ - 2104 K-th Number && POJ 2761 Feed the dogs(主席树静态区间第k大)
- 【主席树 求区间第k大】poj 2104 K-th Number
- 【POJ - 2104】K-th Number 【主席树 求静态区间第k大】
- POJ 2104 K-th Number 主席树(区间第k大)
- POJ 2104(K-th Number-区间第k大-主席树)
- POJ 2104 K-th Number (主席树 静态区间第K大)
- poj2761&&poj2104 主席树(静态区间第K大)
- POJ-2104 K-th Number (主席树 不带修改区间第k大)
- POJ 2104 K-th Number 主席树(求区间第k大)
- POJ 2104(K-th Number-区间第k大-主席树)
- 【POJ 2104】(K-th Number-区间第k大-主席树)
- POJ 2104 K-th Number 主席树 区间第K大
- poj 2104 区间第K小 主席树入门题
- POJ 2104(K-th Number-区间第k大-主席树)
- [poj 2104 K-th Number] 主席树 区间第K大
- poj 2104 K-th Number (划分树入门 或者 主席树入门)
- POJ 2104 【主席树】【区间第K大】
- POJ 2104 K-th Number(区间第k大数)(平方分割,归并树,划分树)