主席树

刚了一下午的主席树,基本熟悉两种经典操作

一.维护区间第k小值;

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=200010;
int n,q,m,cnt=0;
int a[N],b[N],T[N];
int sum[N<<5],L[N<<5],R[N<<5];
int build(int l,int r)
{
    int rt=++cnt;
    if(l<r)
    {
        int mid=(l+r)/2;
        L[rt]=build(l,mid);
        R[rt]=build(mid+1,r);
    }
    return rt;
}
int updata(int l,int r,int pre,int x)
{
    int rt=++cnt;
    L[rt]=L[pre];
    R[rt]=R[pre];
    sum[rt]=sum[pre]+1;
    if(l<r)
    {
        int mid=(l+r)/2;
        if(x<=mid)
            L[rt]=updata(l,mid,L[pre],x);
        else
            R[rt]=updata(mid+1,r,R[pre],x);
    }
    return rt;
}
int query(int l,int r,int l1,int r1,int k)
{
    if(l==r)
        return l;
    int x=sum[L[r1]]-sum[L[l1]];
    int mid=(l+r)/2;
    if(x>=k)
        return query(l,mid,L[l1],L[r1],k);
    else
        return query(mid+1,r,R[l1],R[r1],k-x);
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+1+n);
    m=unique(b+1,b+1+n)-b-1;
    T[0]=build(1,m);
    for(int i=1;i<=n;i++)
    {
        int t=lower_bound(b+1,b+1+m,a[i])-b;
        T[i]=updata(1,m,T[i-1],t);
    }
    for(int i=1;i<=q;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        int t=query(1,m,T[x-1],T[y],z);
        printf("%d\n",b[t]);
    }
    return 0;
}

二.支持单点修改,维护区间第k小值(树状数组套主席树)

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct question
{
	int judge;
	int t,p;
	int x,y,z;
}linker[100001];
struct charman_tree
{
	int val;
	int l;
	int r;
}tr[4001000];
int n,m,q,tot,cnt1,cnt2,x,y,z;
int a[100010],b[100010],T[100010];
int temp[3][20];
char opt;
void updata(int &node,int l,int r,int x,int val)
{
	if(!node)
		node=++tot;
	tr[node].val+=val;
	if(l==r)
		return;
	int mid=(l+r)/2;
	if(x<=mid)
		updata(tr[node].l,l,mid,x,val);
	else
		updata(tr[node].r,mid+1,r,x,val);
}
void add(int loc,int val)
{
	int k=lower_bound(b+1,b+1+m,a[loc])-b;
	for(int i=loc;i<=n;i+=i&-i)
		updata(T[i],1,m,k,val);
}
int query(int l,int r,int k)
{
	if(l==r)
		return l;
	int mid=(l+r)/2,sum=0;
	for(int i=1;i<=cnt1;i++)
		sum-=tr[tr[temp[1][i]].l].val;
	for(int i=1;i<=cnt2;i++)
		sum+=tr[tr[temp[2][i]].l].val;
	if(k<=sum)
	{
		for(int i=1;i<=cnt1;i++)
			temp[1][i]=tr[temp[1][i]].l;
		for(int i=1;i<=cnt2;i++)
			temp[2][i]=tr[temp[2][i]].l;
		return query(l,mid,k);
	}
	else
	{
		for(int i=1;i<=cnt1;i++)
			temp[1][i]=tr[temp[1][i]].r;
		for(int i=1;i<=cnt2;i++)
			temp[2][i]=tr[temp[2][i]].r;
		return query(mid+1,r,k-sum);
	}
}
int ask(int x,int y,int z)
{
	memset(temp,0,sizeof(temp));
	cnt1=cnt2=0;
	for(int i=x-1;i>0;i-=i&-i)
		temp[1][++cnt1]=T[i];
	for(int i=y;i>0;i-=i&-i)
		temp[2][++cnt2]=T[i];
	return query(1,m,z);
}
int main()
{	
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[++m]=a[i];
	}
	for(int i=1;i<=q;i++)
	{
		cin>>opt;
		if(opt=='C')
		{
			scanf("%d%d",&x,&y);
			linker[i].judge=1;
			linker[i].p=x;
			linker[i].t=y;
			b[++m]=y;
		}
		else
		{
			scanf("%d%d%d",&x,&y,&z);
			linker[i].judge=2;
			linker[i].x=x;
			linker[i].y=y;
			linker[i].z=z;
		}
	}
	sort(b+1,b+1+m);
	m=unique(b+1,b+1+m)-b-1;
	for(int i=1;i<=n;i++)
		add(i,1);
	for(int i=1;i<=q;i++)
	{
		if(linker[i].judge==1)
		{
			int x=linker[i].p;
			add(x,-1);
			a[x]=linker[i].t;
			add(x,1);
		}
		else
		{
			int t=ask(linker[i].x,linker[i].y,linker[i].z);
			printf("%d\n",b[t]);
		}
	}
	return 0;
}

查询区间众数

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 500010;
int n,q,m,cnt=0,ans,l,r,k;
int a[N],b[N],root[N];
int sum[N<<5],L[N<<5],R[N<<5];
int build(int l,int r)
{
	int rt=++cnt;
	int mid=(l+r)/2;
	if(l<r)
	{
		L[rt]=build(l,mid);
		R[rt]=build(mid+1,r);
	}
	return rt;
}
int update(int l,int r,int pre,int x)
{
	int rt=++cnt;
	L[rt]=L[pre];
	R[rt]=R[pre];
	sum[rt]=sum[pre]+1;
	if(l<r)
	{
		int mid=(l+r)/2;
		if(x<=mid)
			L[rt]=update(l,mid,L[pre],x);
		else
			R[rt]=update(mid+1,r,R[pre],x);
	}
	return rt;
}
int query(int l,int r,int l1,int r1,int k)
{
	if(l==r)
		return l;
	int mid=(l+r)/2;
	int L1=L[r1],L2=L[l1];
	if(sum[L1]-sum[L2]>k)
		return query(l,mid,L2,L1,k);
	int R1=R[r1],R2=R[l1];
	if(sum[R1]-sum[R2]>k)
		return query(mid+1,r,R2,R1,k);
	return 0;
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
	sort(b+1,b+1+n);
	m=unique(b+1,b+1+n)-b-1;
	root[0]=build(1,m);
	for(int i=1;i<=n;i++)
	{
		int t=lower_bound(b+1,b+1+m,a[i])-b;
		root[i]=update(1,m,root[i-1],t);
	}
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d",&l,&r);
		int k=(r-l+1)/2;
		printf("%d\n",query(1,m,root[l-1],root[r],k));
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值