主席树和划分树板子一

 P3834 【模板】可持久化线段树 1(主席树)

题目背景

这是个非常经典的主席树入门题——静态区间第K小

数据已经过加强,请使用主席树。同时请注意常数优化

题目描述

如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。

输入输出格式

输入格式:

 

第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。

第二行包含N个正整数,表示这个序列各项的数字。

接下来M行每行包含三个整数l, r, kl,r,k , 表示查询区间[l, r][l,r]内的第k小值。

 

输出格式:

 

输出包含k行,每行1个正整数,依次表示每一次查询的结果

 

输入输出样例

输入样例#1: 复制
5 5
25957 6405 15770 26287 26465 
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1
输出样例#1: 复制
6405
15770
26287
25957
26287

说明

数据范围:

对于20%的数据满足:1 \leq N, M \leq 101N,M10

对于50%的数据满足:1 \leq N, M \leq 10^31N,M103

对于80%的数据满足:1 \leq N, M \leq 10^51N,M105

对于100%的数据满足:1 \leq N, M \leq 2\cdot 10^51N,M2105

对于数列中的所有数a_iai,均满足-{10}^9 \leq a_i \leq {10}^9109ai109

样例数据说明:

N=5,数列长度为5,数列从第一项开始依次为[25957, 6405, 15770, 26287, 26465 ][25957,6405,15770,26287,26465]

第一次查询为[2, 2][2,2]区间内的第一小值,即为6405

第二次查询为[3, 4][3,4]区间内的第一小值,即为15770

第三次查询为[4, 5][4,5]区间内的第一小值,即为26287

第四次查询为[1, 2][1,2]区间内的第二小值,即为25957

第五次查询为[4, 4][4,4]区间内的第一小值,即为26287

主席树真是个好东西,比划分树不知高到哪里去.

主席树

#include<stdio.h>
#include<algorithm>
#define FOR(i,s,t) for(register int i=s;i<=t;++i)
using std::sort;
using std::lower_bound;
using std::unique;
const int NlogN=3521999;
const int N=200011;
int a[N],b[N];
int n,m,len,tot;
int l,r,k;
int rt[N];
inline void disc_init(){
	sort(b+1,b+n+1);
	len=unique(b+1,b+n+1)-b-1;
	FOR(i,1,n)a[i]=lower_bound(b+1,b+len+1,a[i])-b;
}
struct Tree{
	int ls,rs,sum;
}tr[NlogN<<2];
inline void insert(int &x,int &y,int l,int r,int p){
	tr[x=++tot]=tr[y];
	++tr[x].sum;
	if(l==r)return;
	int mid=(l+r)>>1;
	p<=mid?insert(tr[x].ls,tr[y].ls,l,mid,p):insert(tr[x].rs,tr[y].rs,mid+1,r,p);
}
inline void build_tree(){
	FOR(i,1,n)insert(rt[i],rt[i-1],1,len,a[i]);
}
inline int query(int x,int y,int l,int r,int p){
	if(l==r)return l;
	int mid=(l+r)>>1;
	int data=tr[tr[x].ls].sum-tr[tr[y].ls].sum;
	return p<=data?query(tr[x].ls,tr[y].ls,l,mid,p):query(tr[x].rs,tr[y].rs,mid+1,r,p-data);
}
int main(){
	scanf("%d%d",&n,&m);
	FOR(i,1,n)scanf("%d",a+i),b[i]=a[i];
	disc_init();
	build_tree();
	while(m--){
		scanf("%d%d%d",&l,&r,&k);
		printf("%d\n",b[query(rt[r],rt[l-1],1,len,k)]);
	}
	return 0;
}

  

划分树

#include<stdio.h>
#include<algorithm>
#define FOR(i,s,t) for(register int i=s;i<=t;++i)
using std::sort;
const int N=1000011;
typedef long long LL;
int a[N];
int sorted[N];
int num[21][N];
int val[21][N];
inline void build(int l,int r,int dep){
    if(l==r) return ;
    int mid=(l+r)>>1,isame=mid-l+1;
    FOR(i,l,r) 
        if(val[dep][i]<sorted[mid]) --isame;
    int ln=l,rn=mid+1;
    FOR(i,l,r){
        i==l?num[dep][i]=0:num[dep][i]=num[dep][i-1];
    	if(val[dep][i]<sorted[mid]||(val[dep][i]==sorted[mid]&&isame>0)){
       		val[dep+1][ln++]=val[dep][i];
      		++num[dep][i];
          	if(val[dep][i]==sorted[mid]) 
              	--isame;
        }
        else
         	val[dep+1][rn++]=val[dep][i];
    }
    build(l,mid,dep+1);
    build(mid+1,r,dep+1);
}
inline int query(int dep,int sl,int sr,int l,int r,int k){
    if(sl==sr) return val[dep][sl];
    int ly;
    l==sl?ly=0:ly=num[dep][l-1];
    int tolef=num[dep][r]-ly;
    int mid=(sl+sr)>>1;
    int lr=mid+1+(l-sl-ly);
    return tolef>=k?query(dep+1,sl,mid,sl+ly,sl+num[dep][r]-1,k):query(dep+1,mid+1,sr,lr,lr+r-l-tolef,k-tolef);
}
int main(){
    int n,m,l,r,k;
    scanf("%d%d",&n,&m);
    FOR(i,1,n){
        scanf("%d",&val[0][i]);
        sorted[i]=val[0][i];
    }
    sort(sorted+1,sorted+n+1);
    build(1,n,0);
    while(m--){
        scanf("%d%d%d",&l,&r,&k);
        printf("%d\n",query(0,1,n,l,r,k));
    }
    return 0;
}

  

posted @ 2017-11-18 15:47  Stump  阅读(471)  评论(0编辑  收藏  举报