poj2104 主席树(还是不太会)

这篇博客写的挺好的 推荐去看 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;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值