这篇博客写的挺好的 推荐去看 https://blog.csdn.net/woshinannan741/article/details/53012682
Input
The first line of the input file contains n — the size of the array, and m — the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000).
The second line contains n different integer numbers not exceeding 109 by their absolute values — the array for which the answers should be given.
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).
Output
For each question output the answer to it — the k-th number in sorted a[i…j] segment.
Sample Input
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
Sample Output
5
6
3
主席树的基本姿势 q& a:
1、离散化 :
要求有一个函数能够查询到这个数离散化后的数值
public class sixth {
static class Node implements Comparable<Node>{
int rt;
int idx;
public Node(int rt, int idx) {
this.rt = rt;
this.idx = idx;
}
@Override
public int compareTo(Node node) {
return rt-node.rt;
}
}
public static void work(int[] array){
int[] rank=new int[array.length];
Node[] nodes=new Node[array.length];
for(int i=0;i<array.length;++i){
nodes[i]=new Node(array[i],i); //传入数值和坐标
}
java.util.Arrays.sort(nodes); //排序 记得实现Comparable接口 以rt大小排序
for(int i=0;i<array.length;++i){
rank[nodes[i].idx]=i;
}
for(int i=0;i<array.length;++i){
array[i]=rank[i]+1;
}
for(int i = 0;i<array.length;i++){
System.out.println(array[i]);
}
//10000000,2,2,123123213 离散化成 3 1 2 4
}
public static void main(String[] args) {
int[] a={10000000,2,2,123123213};
work(a);
}
}
2、
何储存主席树?
建立一个数组root[maxn] 来储存各个根节点的编号。对于儿子结点 不能用当前节点编号成2得到了,于是,我们可以用一个struct储存当前节点的左右儿子结点的编号和当前节点的值,然后再用这个struct 开一个内存池,每次新建一个主席树的节点 就从内存池中取出一块新的空间送给这个节点。取空间从hjt【1】开始 hjt【0】充当null,
//存储主席树
static class Node{
int l,r,sum; //该节点的左孩子是 hjt[l],右节点是hjt[r],值为sum ????不应该是node.??
}
static int maxn = 100010;
static Node hjt[] = new Node[maxn*40];
static int cnt; //内存池计数器
static int[] root= new int[maxn]; //根节点编号
3、主席树用不用像线段树那样用一个build函数,先把数据读进去建树先,但主席树不需要,准确的说程序已经建好了,刚开始所有节点的l,r,sum都是0,而全局变量和数组默认赋值为0,所以我们只需要边插入,边建树就Ok了, 读一个差一个
4、用不用进行两树减法,建立一个新的权值线段树使其等于两主席树只差 然后对新的权值线段树进行询问 ,
其实不用 ,可以边递归,边减
所以:完整代码:
这个代码是自己写的 有点问题的,再改改,,,
import java.util.Scanner;
public class sixth {
static int n,q;
static int maxn = 100010;
static int [] a = new int[maxn];
public static void main(String[] args) {
Scanner sc= new Scanner(System.in);
n = sc.nextInt();
q = sc.nextInt();
for(int i =1;i<= n;i++){
a[i] = sc.nextInt();
}
// int array [] = work(a);
for(int i = 0;i<a.length;i++ ){
insert(1,n,root[i-1],root[i],work(a,a[i]));
}
while(q-->0){
int l = sc.nextInt();
int r = sc.nextInt();
int k = sc.nextInt();
int x = query(l,r,root[l-1],root[r],k);
System.out.println(a[x]);//因为那个x返回的是位置,而要返回的是实际的数
}
}
static int query(int l,int r,int L,int R,int k){ //L表示l-1那个版本的权值线段树遍历的当前节点,R表示
if(l==r) return l;
int m = (l+r)>>1;
int tmp = hjt[hjt[R].l].sum - hjt[hjt[L].l].sum; //这个是左边的sum和
if(k<=tmp) return query(l,m,hjt[L].l,hjt[R].l,k);
else return query(m+1,r,hjt[L].r,hjt[R].r,k-tmp);
}
static void insert(int l,int r,int pre, int now,int p){ //当前节点维护l到r,当前节点是now,要插入的是p,
hjt[++cnt] = hjt[pre]; //上一个树的左边的节点他都有,,不太理解哎,这个代码写法,不应该是全部他都有吗
now= cnt;
hjt[now].sum++;
if(r == l) return;
int m = (l+r)>>1;
if(p<=m) insert(l,m,hjt[pre].l,hjt[now].l,p);
else insert(m+1,r,hjt[pre].r,hjt[now].r,p);
}
static class node implements Comparable<node>{
int rt;
int idx;
public node(int rt, int idx) {
this.rt = rt;
this.idx = idx;
}
@Override
public int compareTo(node node) {
return rt-node.rt;
}
}
public static int work(int[] array,int x){
int[] rank=new int[array.length];
node[] nodes=new node[array.length];
for(int i=0;i<array.length;++i){
nodes[i]=new node(array[i],i); //传入数值和坐标
}
java.util.Arrays.sort(nodes); //排序 记得实现Comparable接口 以rt大小排序
for(int i=0;i<array.length;++i){
rank[nodes[i].idx]=i;
}
for(int i=0;i<array.length;++i){
array[i]=rank[i]+1;
}
return array[x]; //这里有一点慌,是x还是x+1
// for(int i = 0;i<array.length;i++){
// System.out.println(array[i]);
// }
//10000000,2,2,123123213 离散化成 3 1 2 4
}
//存储主席树
static class Node{
int l,r,sum; //该节点的左孩子是 hjt[l],右节点是hjt[r],值为sum ????不应该是node.??
}
static Node hjt[] = new Node[maxn*40];
static int cnt; //内存池计数器
static int[] root= new int[maxn]; //根节点编号
}
这个是仿照一位大佬写的,同须改
import java.util.*;
class third{
static int n;
static int q;
static int max = 100010;
static int N = max*4;
static int T[] = new int[max];
static int t[] = new int[max];
static int sum[] = new int[N];
static int lson[],rson[],getRson = N;
static ArrayList<Integer> temp_list = new ArrayList<>();
static int[] a = new int[max];
static int []temp = new int[max];
static int tot,m;
public static void main(String[] args) {
Scanner sc= new Scanner(System.in);
n = sc.nextInt();
q = sc.nextInt();
for(int i =1;i<= n;i++){
a[i] = sc.nextInt();
}
ArrayList<Integer> temp_list = unique(a);
int list_len = temp_list.size();
m= list_len;
Arrays.sort(new ArrayList[]{temp_list});
for(int i = 1;i < list_len;i++){
temp[i] = temp_list.get(i);
System.out.println(temp[i]);
}
int m = temp_list.size();
T[0] = build(1,m); //第0课树
for(int i =1;i<=n;i++){//建树过程
T[i] =update(T[i-1],getid(a[i])); //每一次建新的树都是在之前的树的基础上建的
}
while(q-->0){
int x = sc.nextInt();
int y = sc.nextInt();
int k = sc.nextInt();
System.out.println(temp[query(T[x-1],T[y],k)-1]);
}
}
static int query(int lrt,int rrt,int k){ //没动?????
int l = 1,r = m;
while (l<r){
int mid = (l+r)>>1;
int cnt = sum[lson[rrt]] - sum[lson[lrt]]; //lson 数组的意义???
if(cnt>=k){
r = mid;
lrt = lson[lrt];
rrt = lson[rrt];
}else {
l = mid+1;
k-=cnt;
lrt = rson[lrt];
rrt = rson[rrt];
}
}
return 1;
}
static int getid(int x ){ //离散化 ,不是必须
return Arrays.binarySearch(temp,x)+1; //???这个不太对哎,但是我没想通离散话的过程 return lower_bound(V.begin(),V.end(),x)-V.begin()+1;
}
static int update(int rt,int pos){ //把数组中的元素一次加入新的线段树中 ??????????/不太懂操作的过程
int nrt = tot++; //???????
int tmp =nrt;
sum[nrt] = sum[rt]+1;
int l = 1,r= m;
while (l<r){
int mid = (l+r)/2;
if(pos<=mid){
lson[nrt] = tot++;
rson[nrt] = rson[rt];
nrt= lson[nrt];
rt =lson[rt];
r = mid;
}else {
rson[nrt] = tot++;
lson[nrt] = lson[rt];
nrt = rson[nrt];
rt = rson[rt];
l=mid+1;
}
sum[nrt] = sum[rt]+1;
}
return tmp;
}
static int build(int l,int r){
int rt = tot++;//记录这是第几个树,方便下一个树在这个树的基础上建树,但是这个数一开始第一次建树就记录了这棵树有多少颗小树
sum[rt] = 0;//????????
if(l!=r){
int mid =(l+r)/2;
lson[rt] =build(l,mid);
rson[rt] = build(mid+1,r);
}
return rt;
}
static ArrayList<Integer> unique(int []a){
Map<Integer,Integer> map = new HashMap<Integer, Integer>();
for (int x : a) {
map.put(x, 1);
}
Set<Integer> keyset = map.keySet();
Iterator<Integer> it1 = keyset.iterator();
int x = 0;
while (it1.hasNext()){
int id= it1.next();
temp_list.add(id);
}
return temp_list;
}
}