(数据结构)静态主席树和一些入门题

主席树真的神奇!

直接给我经常用的板子吧

静态主席树:点这里 -----点这个也不错
动态主席树:点这里

/* 	
静态主席树(查询区间第 k 大)
/*
	在STL中unique函数是一个去重函数, unique的功能是去除相邻的重复元素(只保留一个),
	其实它并不真正把重复的元素删除,是把重复的元素移到后面去了,
	然后依然保存到了原数组中,然后 返回去重后最后一个元素的地址,去重后最后一个元素的下一个地址 
	因为unique去除的是相邻的重复元素,所以一般用之前都会要排一下序。
*/
/*
	n 是大小!然后m是离散化后的个数
	ls[],rs[] 分别表示的就是左儿子跟右儿子
	c[]  记入的就是数字出现的次数
	你要算区间[x,y],那么就是temp = c[ls[T[y]]-c[ls[T[x-1]]  然后判断跟k的大小	利用的就是前缀和的思想
	tot 表示的就是所有的节点 
*/ 
const int maxn=1e5+10;
const int N=maxn*30;
int n,q,m,tot;
int a[maxn],t[maxn];
int T[N],ls[N],rs[N],c[N];
 
void init()
{
	for(int i=1;i<=n;i++)
	t[i]=a[i];
	sort(t+1,t+n+1);
	m=unique(t+1,t+n+1)-t-1;
}
 
int build(int l,int r)   
{
	int root=tot++;
	c[root]=0;
	if(l!=r)
	{
		int mid=(l+r)/2;
		ls[root]=build(l,mid);
		rs[root]=build(mid+1,r);
	}
	return root;
}
 
int hashh(int x)
{
	return lower_bound(t+1,t+1+m,x)-t;   //返回下标 
} 
 
int update(int root,int pos,int val,int l,int r)
{
	int newroot=tot++,tmp=newroot;
	c[newroot]=c[root]+val;
	if(l==r) return 1;
	int mid=(l+r)/2;
	if(pos<=mid)
	{
		ls[newroot]=tot;rs[newroot]=rs[root];
		update(ls[root],pos,val,l,mid);
	}
	else
	{
		rs[newroot]=tot;ls[newroot]=ls[root];
		update(rs[root],pos,val,mid+1,r);
	}
	return tmp;
} 
 
int query(int l,int r,int x,int y,int k)	// 查找区间 X,y 
{
	int mid=(l+r)/2;
	if(l==r) return l;
	int sum=c[ls[y]]-c[ls[x]];
	if(sum>=k)
	{
		query(l,mid,ls[x],ls[y],k);
	}
	else
	query(mid+1,r,rs[x],rs[y],k-sum);
	
}

void solve(){
	
	scanf("%d%d",&n,&q);
    tot=0;
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    init();
    T[0]=build(1,m);
    //cout<<T[0]<<endl;
    for(int i=1;i<=n;i++)
    {
    	int pos=hashh(a[i]);
    	T[i]=update(T[i-1],pos,1,1,m);
    	//cout<<T[i]<<" ";
	}
	//cout<<endl;
	while(q--)
	{
		int l,r,k;
	    scanf("%d%d%d",&l,&r,&k);
	    printf("%d\n",t[query(1,m,T[l-1],T[r],k)]);
	} 
} 

静态主席树第一题

裸题:Hdu Kth number---- 洛谷3834
题目传送门POJ2104
题意: 给你n,m,让你去求区间第k大的数字
我的理解: 直接看我套的板子吧!

AC代码

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<string>
#include<set>
#define sa(t) scanf("%d",&t)
#define SA(t) scanf("%lld", %t)
#define PF(t) printf("%lld",t)
#define pf(t) printf("%d", t)
#define PFF(t) printf("%lld\n",t)
#define pff(t) printf("%d\n", t)
typedef long long int ll;

using namespace std;
const int maxn=1e5+10;
const int N=maxn*30;
int n,q,m,tot;
int a[maxn],t[maxn];
int T[N],ls[N],rs[N],c[N];
 
void init()
{
	for(int i=1;i<=n;i++)
	t[i]=a[i];
	sort(t+1,t+n+1);
	m=unique(t+1,t+n+1)-t-1;
}
 
int build(int l,int r)   
{
	int root=tot++;
	c[root]=0;
	if(l!=r)
	{
		int mid=(l+r)/2;
		ls[root]=build(l,mid);
		rs[root]=build(mid+1,r);
	}
	return root;
}
int hashh(int x)
{
	return lower_bound(t+1,t+1+m,x)-t;   //返回下标 
} 
 
int update(int root,int pos,int val,int l,int r)
{
	int newroot=tot++,tmp=newroot;
	c[newroot]=c[root]+val;
	if(l==r) return 1;
	int mid=(l+r)/2;
	if(pos<=mid)
	{
		ls[newroot]=tot;rs[newroot]=rs[root];
		update(ls[root],pos,val,l,mid);
	}
	else
	{
		rs[newroot]=tot;ls[newroot]=ls[root];
		update(rs[root],pos,val,mid+1,r);
	}
	return tmp;
} 
 
int query(int l,int r,int x,int y,int k)	// 查找区间 X,y 
{
	int mid=(l+r)/2;
	if(l==r) return l;
	int sum=c[ls[y]]-c[ls[x]];
	if(sum>=k)
	{
		query(l,mid,ls[x],ls[y],k);
	}
	else
	query(mid+1,r,rs[x],rs[y],k-sum);
	
}

void solve(){
	
	scanf("%d%d",&n,&q);
    tot=0;
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    init();
    T[0]=build(1,m);
    //cout<<T[0]<<endl;
    for(int i=1;i<=n;i++)
    {
    	int pos=hashh(a[i]);
    	T[i]=update(T[i-1],pos,1,1,m);
    	//cout<<T[i]<<" ";
	}
	//cout<<endl;
	while(q--)
	{
		int l,r,k;
	    scanf("%d%d%d",&l,&r,&k);
	    printf("%d\n",t[query(1,m,T[l-1],T[r],k)]);
	} 
} 
int main(){
 
 	int cass;
 	cass = 1;
	// scanf("%d",&cass);
 	while(cass--){
 		solve();
	}
    
}

持续更新中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值