主席树|求区间第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;
} 

原文地址:https://www.cnblogs.com/fisherss/p/12229528.html

时间: 2024-12-18 16:02:29

主席树|求区间第k小模板的相关文章

poj 2104主席树求区间第k小

POJ - 2104 题意:求区间第k小 思路:无修改主席树 AC代码: #include "iostream" #include "iomanip" #include "string.h" #include "stack" #include "queue" #include "string" #include "vector" #include "set&

HDU 5919 - Sequence II (2016CCPC长春) 主席树 (区间第K小+区间不同值个数)

HDU 5919 题意: 动态处理一个序列的区间问题,对于一个给定序列,每次输入区间的左端点和右端点,输出这个区间中:每个数字第一次出现的位子留下, 输出这些位子中最中间的那个,就是(len+1)/2那个. 思路: 主席树操作,这里的思路是从n到1开始建树.其他就是主席树查询区间第K小,计算区间不同值个数. #include <algorithm> #include <iterator> #include <iostream> #include <cstring&

SPOJ 10628 Count on a tree(Tarjan离线LCA+主席树求树上第K小)

COT - Count on a tree #tree You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight. We will ask you to perform the following operation: u v k : ask for the kth minimum weight on the path from node u 

主席树(区间第k小的数)

题目链接: https://www.luogu.org/problem/P3834 首先要离散化,然后主席树模板. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define mid (l+r)/2 5 using namespace std; 6 7 const int N = 200010; 8 int n, q, m, cnt = 0; 9 int a[N], b[N], T[N];

洛谷.3834.[模板]可持久化线段树(主席树 静态区间第k小)

题目链接 //离散化后范围1~cnt不要错 #include<cstdio> #include<cctype> #include<algorithm> //#define gc() getchar() #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++) const int N=2e5+5,MAXIN=2e6; int n,m,A[N],ref[N],cn

HDOJ题目4417 Super Mario(划分树求区间比k小的个数+二分)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3313    Accepted Submission(s): 1548 Problem Description Mario is world-famous plumber. His "burly" figure and amazing jumping a

hdu 2665 可持久化线段树求区间第K大值(函数式线段树||主席树)

http://acm.hdu.edu.cn/showproblem.php?pid=2665 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first line is the number of the test cases. For each test case, the first line contain two integer n and m (

poj 2401 划分树 求区间第k大的数

题目:http://poj.org/problem?id=2104 划分树待我好好理解下再写个教程吧,觉得网上的内容一般,,, 模板题: 贴代码: #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define CLR(a) memset(a,0,sizeof(a)) const int MAXN = 1000

[csu/coj 1080]划分树求区间前k大数和

题意:从某个区间内最多选择k个数,使得和最大 思路:首先题目给定的数有负数,如果区间前k大出现负数,那么负数不选和更大,于是对于所有最优选择,负数不会出现,所以用0取代负数,问题便转化为区间的前k大数和. 划分树: [1  6  3  8  5  4  7  2] [6  8  5  7][1  3  4  2] [8  7][6  5][3  4][1  2] [8][7][6][5][4][3][2][1] 把快排的结果从上至下依次放入线段树,就构成了划分树,划分的意思就是选定一个数,把原序