不带修改主席树模板

43 篇文章 0 订阅
39 篇文章 6 订阅

对于一部分线段树看似无法直接做的题,可以用主席树来做。
主席树就是对每个前缀开一棵线段树,当然,直接这样会MLE。
可以使用一种类似动态开节点的方法可以有效避免MLE。
具体可以参考我的博客,那里写的更详细一点:可持久化线段树
因为主席树是由前缀加起来的,所以区间[l,r]的解可以类似前缀和那样a[r]-a[l-1]直接减
主席树的详细解答可以看看这里

也可以直接点这里

建树
主程序

fo(i,1,n) {
        root[i]=++tot;t[tot]=t[root[i-1]];
        build(root[i],1,n,c[i]);
    }

build

void build(int v,int i,int j,int z)
{
    t[v].d++;if(i==j) return;
    int m=(i+j)/2;
    if(z<=m) t[++tot]=t[t[v].l],t[v].l=tot,build(t[v].l,i,m,z);

查询请直接看下面的完整代码

模板题:不带修改的区间K大(小)数查询 poj2104
题目链接

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 101000
using namespace std;
int n,a[N],b[N],c[N],root[N],tot=0;
struct note{
    int l,r,d;
};
note t[N*50];
bool cnt(int i,int j){return a[i]<a[j];}
void build(int v,int i,int j,int z)
{
    t[v].d++;if(i==j) return;
    int m=(i+j)/2;
    if(z<=m) t[++tot]=t[t[v].l],t[v].l=tot,build(t[v].l,i,m,z);
    else t[++tot]=t[t[v].r],t[v].r=tot,build(t[v].r,m+1,j,z);
}
int find(int v1,int v2,int i,int j,int k)
{ 
    if(i==j) return i;
    int jy=t[t[v2].l].d-t[t[v1].l].d,m=(i+j)/2;
    if (jy>=k) return find(t[v1].l,t[v2].l,i,m,k);
    else return find(t[v1].r,t[v2].r,m+1,j,k-jy);
}
int main()
{
    int m;scanf("%d%d",&n,&m);
    fo(i,1,n) scanf("%d",&a[i]),b[i]=i;
    sort(b+1,b+n+1,cnt);
    fo(i,1,n) c[b[i]]=i;
    fo(i,1,n) {
        root[i]=++tot;t[tot]=t[root[i-1]];
        build(root[i],1,n,c[i]);
    }
    for(;m;m--)
    {
        int x,y,k;scanf("%d%d%d",&x,&y,&k);
        printf("%d\n",a[b[find(root[x-1],root[y],1,n,k)]]);

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值