zoukankan      html  css  js  c++  java
  • 主席树|求区间第k小模板

    主席树

    学了主席树,用来求区间上的第k小
    写一下自己整理后的模板

    求区间第k小

    #include<bits/stdc++.h>
    using namespace std;
    
    //求区间第k小 
    
    const int maxn = 500010;
    struct node{
    	int v,lc,rc;
    }T[maxn * 21];
    int n,m;
    int root[maxn];
    int e; 
    
    void insert(int pre,int cur,int pos,int l,int r){
    	if(l == r){
    		T[cur].v = T[pre].v + 1;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(pos <=mid ){
    		T[cur].lc = ++e; //新建左子结点 
    		T[cur].rc = T[pre].rc; //保留右子节点 
    		insert(T[pre].lc,T[cur].lc,pos,l,mid); //insert左 
    	}else{
    		T[cur].rc = ++e; //新建右子节点 
    		T[cur].lc = T[pre].lc; //保留左子结点 
    		insert(T[pre].rc,T[cur].rc,pos,mid+1,r); //insert右 
    	}
    	T[cur].v = T[T[cur].lc].v + T[T[cur].rc].v;
    }
    
    
    int query(int ql,int qr,int l,int r,int k){
    	if(l == r) return l;
    	int mid = (l + r) >> 1;
    	int sum = T[T[qr].lc].v - T[T[ql].lc].v;
    	if(sum >= k) return query(T[ql].lc,T[qr].lc,l,mid,k);
    	else return query(T[ql].rc,T[qr].rc,mid+1,r,k-sum);  //这个ql.rc 和 qr.lc什么时候用老是搞不清楚 
    } 
    
    
    int main(){
    	int x,y,d,k;
    	ios::sync_with_stdio(false);
    	cin>>n>>m;
    	for(int i=1;i<=n;i++){
    		cin>>d;
    		root[i] = ++e;
    		insert(root[i-1],root[i],d,1,n);
    	}
    	while(m--){
    		cin>>x>>y>>k;
    		cout<<query(root[x-1],root[y],1,n,k)<<endl;
    	}
    	return 0;
    } 
    

    SPOJ 3946主席树 + 离散化

    当数据比较大时(比如这道题的1e9那么大),就需要先离散化了。

    #include<bits/stdc++.h>
    using namespace std;
    //sp3946
    
    const int maxn = 500010;
    struct node{
    	int v,lc,rc;
    }T[maxn * 21];
    int n,m;
    int root[maxn];
    int ranks[maxn];
    int e; 
    
    struct A{
    	int x,idx;
    	bool operator < (const A &temp)const{
    		return x < temp.x;
    	}
    }a[maxn];
    
    void insert(int pre,int cur,int pos,int l,int r){
    	if(l == r){
    		T[cur].v = T[pre].v + 1;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(pos <=mid ){
    		T[cur].lc = ++e; //新建左子结点 
    		T[cur].rc = T[pre].rc; //保留右子节点 
    		insert(T[pre].lc,T[cur].lc,pos,l,mid); //insert左 
    	}else{
    		T[cur].rc = ++e; //新建右子节点 
    		T[cur].lc = T[pre].lc; //保留左子结点 
    		insert(T[pre].rc,T[cur].rc,pos,mid+1,r); //insert右 
    	}
    	T[cur].v = T[T[cur].lc].v + T[T[cur].rc].v;
    }
    
    int query(int ql,int qr,int l,int r,int k){
    	if(l == r) return l;
    	int mid = (l + r) >> 1;
    	int sum = T[T[qr].lc].v - T[T[ql].lc].v;
    	if(sum >= k) return query(T[ql].lc,T[qr].lc,l,mid,k);
    	else return query(T[ql].rc,T[qr].rc,mid+1,r,k-sum);  //这个ql.rc 和 qr.lc什么时候用老是搞不清楚 
    } 
    
    int main(){
    	int x,y,d,k;
    	ios::sync_with_stdio(false);
    	cin>>n>>m;
    	for(int i=1;i<=n;i++){
    		cin>>a[i].x;
    		a[i].idx = i;
    	}
    	sort(a+1,a+n+1);
    	for(int i=1;i<=n;i++) ranks[a[i].idx] = i;
    	for(int i=1;i<=n;i++){
    		root[i] = ++e;
    		insert(root[i-1],root[i],ranks[i],1,n);
    	}
    	while(m--){
    		cin>>x>>y>>k;
    		cout<<query(root[x-1],root[y],1,n,k)<<endl;
    	}
    	return 0;
    } 
    

    poi2014 出现次数次数>(r-l+1)/2

    因为这个数 在区间(l,r)上出现的次数最多为1次或者0次
    所以用主席树维护每个数出现的次数,然后query查询时 左子树出现次数>goal就往左走、右子树出现次数>goal就往右走,否则return 0,最后直到叶节点(l==r)就会找到目标数

    #include<bits/stdc++.h>
    using namespace std;
    //poi2014
    
    const int maxn = 500001;
    struct node{
    	int v,lc,rc;
    }T[maxn * 21];
    int n,m;
    int root[maxn];
    int e; 
    
    void insert(int pre,int cur,int pos,int l,int r){
    	if(l == r){
    		T[cur].v = T[pre].v + 1;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(pos <=mid ){
    		T[cur].lc = ++e; //新建左子结点 
    		T[cur].rc = T[pre].rc; //保留右子节点 
    		insert(T[pre].lc,T[cur].lc,pos,l,mid); //insert左 
    	}else{
    		T[cur].rc = ++e; //新建右子节点 
    		T[cur].lc = T[pre].lc; //保留左子结点 
    		insert(T[pre].rc,T[cur].rc,pos,mid+1,r); //insert右 
    	}
    	T[cur].v = T[T[cur].lc].v + T[T[cur].rc].v;
    }
    
    int goal;
    
    //对于读入的区间,取长度后进行询问
    //如果左子树大于mid的就继续往左走
    //右子树大于mid的就继续往右走,否则return 0
    int query(int ql,int qr,int l,int r){ //这里 T数组两棵树相减  为什么左子树相减  右子树相减 没搞懂 
    	if(l == r) return l;
    	int mid = (l + r) >> 1; 
    	if(T[T[qr].lc].v - T[T[ql].lc].v > goal) return query(T[ql].lc,T[qr].lc,l,mid);
    	else if(T[T[qr].rc].v - T[T[ql].rc].v > goal) return query(T[ql].rc,T[qr].rc,mid+1,r);
    	return 0;  //没有就返回0 
    } 
    
    int main(){
    	int x,y,d;
    	ios::sync_with_stdio(false);
    	cin>>n>>m;
    	for(int i=1;i<=n;i++){
    		cin>>d;
    		root[i] = ++e;
    		insert(root[i-1],root[i],d,1,n);
    	}
    	while(m--){
    		cin>>x>>y;
    		goal = y-x+1>>1;
    		cout<<query(root[x-1],root[y],1,n)<<endl;
    	}
    	return 0;
    } 
    
  • 相关阅读:
    Go语言标准库flag基本使用
    GO学习-(12) Go语言基础之函数
    GO学习-(11) Go语言基础之map
    GO学习-(10) Go语言基础之指针
    Spring AOP
    JDK动态代理
    版本控制
    版本控制
    浅析Java反射机制
    Spring Batch学习
  • 原文地址:https://www.cnblogs.com/fisherss/p/12229528.html
Copyright © 2011-2022 走看看