主席树

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <cstdlib>
#define maxn 100005
using namespace std;
int b[maxn],t[maxn],n,m,size,v[maxn],tot=0;
struct chairtree
{
    int l,r,size;
}a[maxn*30];
int Build(int l,int r)
{
    int root=++tot;
    a[root].size=0;
    if (l==r) return root;
    int m=(l+r)>>1;
    a[root].l=Build(l,m);
    a[root].r=Build(m+1,r);
    return root;
}
int Update(int root,int x)
{
    int now=++tot;
    int tmp=now;
    a[tot].size=a[root].size+1;
    int l=1,r=size;
    while (l<r)
    {
        int m=(l+r)>>1;
        if (x<=m)
        {
            a[now].l=++tot;
            a[now].r=a[root].r;   //充分利用原来相同的结点
            root=a[root].l;
            now=tot;
            r=m;
        }
        else
        {
            a[now].l=a[root].l;
            a[now].r=++tot;
            root=a[root].r;
            now=tot;
            l=m+1;
        }
        a[now].size=a[root].size+1;
    }
    return tmp;
}
int Ask(int lx,int rx,int k)
{
    int l=1,r=size;
    while (l<r)
    {
        int m=(l+r)>>1;
        if (a[a[rx].l].size-a[a[lx].l].size>=k)
        {
            r=m;
            lx=a[lx].l;
            rx=a[rx].l;
        }
        else
        {
            l=m+1;
            k-=a[a[rx].l].size-a[a[lx].l].size;
            lx=a[lx].r;
            rx=a[rx].r;
        }
    }
    return l;
}
void Hash1()
{
    sort(b+1,b+1+n);
    size=unique(b+1,b+1+n)-b-1;  //求不相同的数有几个
}
int Hash(int x)
{
    return lower_bound(b+1,b+1+size,x)-b;   //找到x在排好序的序列中出现的位置,即离散化后的值
}
int main()
{
    while (scanf("%d%d",&n,&m)==2)
    {
        for (int i=1;i<=n;i++)
            scanf("%d",&v[i]),b[i]=v[i];
        Hash1();
        t[0]=Build(1,size);
        for (int i=1;i<=n;i++)   //建n棵线段树
            t[i]=Update(t[i-1],Hash(v[i]));
        while (m--)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",b[Ask(t[l-1],t[r],k)]);
        }
    }
    return 0;
}

主席树求区间第K大

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#define maxn 10005
using namespace std;
int use[maxn*10],n,m,size,tot=0,all=0,h[maxn*10],v[maxn*10],t[maxn*10];
struct chairtree
{
    int l,r,size;
}a[maxn*300];
struct question
{
    int l,r,k;
}q[maxn];
int lowbit(int x)
{
    return x&(-x);
}
void Hash1()
{
    sort(h+1,h+1+all);
    size=unique(h+1,h+1+all)-h-1;
}
int Hash(int x)
{
    return lower_bound(h+1,h+1+size,x)-h;
}
int Build(int l,int r)
{
    int now=++tot;
    a[now].size=0;
    if (l==r) return now;
    int m=(l+r)>>1;
    a[now].l=Build(l,m);
    a[now].r=Build(m+1,r);
    return now;
}
int Update(int root,int p,int val)
{
    int now=++tot,tmp=now;
    int l=1,r=size;
    a[now].size=a[root].size+val;
    while (l<r)
    {
        int m=(l+r)>>1;
        if (p<=m)
        {
            a[now].l=++tot;
            a[now].r=a[root].r;
            root=a[root].l;
            now=a[now].l;
            r=m;
        }
        else
        {
            a[now].l=a[root].l;
            a[now].r=++tot;
            root=a[root].r;
            now=a[now].r;
            l=m+1;
        }
        a[now].size=a[root].size+val;
    }
    return tmp;
}
void Add(int x,int p,int val)
{
    for (int i=x;i<=n;i+=lowbit(i))    //树状数组只需修改logn个
        t[i]=Update(t[i],p,val);
}
int Getsum(int x)
{
    int ans=0;
    for (int i=x;i;i-=lowbit(i))
        ans+=a[a[use[i]].l].size;
    return ans;
}
int Query(int lx,int rx,int k)
{
    int l=1,r=size;
    for (int i=lx-1;i;i-=lowbit(i)) use[i]=t[i];
    for (int i=rx;i;i-=lowbit(i)) use[i]=t[i];
    while (l<r)
    {
        int m=(l+r)>>1;
        int tmp=Getsum(rx)-Getsum(lx-1);
        if (tmp>=k)
        {
            for (int i=lx-1;i;i-=lowbit(i))
                use[i]=a[use[i]].l;
            for (int i=rx;i;i-=lowbit(i))
                use[i]=a[use[i]].l;
            r=m;
        }
        else
        {
            for (int i=lx-1;i;i-=lowbit(i))
                use[i]=a[use[i]].r;
            for (int i=rx;i;i-=lowbit(i))
                use[i]=a[use[i]].r;
            k-=tmp;
            l=m+1;
        }
    }
    return l;
}
int main()
{
        scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&v[i]),h[i]=v[i];
    all=n;
    for (int i=1;i<=m;i++)
    {
        char str[10];
        int l,r,k;
        scanf("%s",str);
        if (str[0]==‘Q‘)
        {
            scanf("%d%d%d",&l,&r,&k);
            q[i].l=l,q[i].r=r,q[i].k=k;
        }
        else
        {
            scanf("%d%d",&r,&k);
            q[i].l=0,q[i].r=r,q[i].k=k;
                        h[++all]=k;
        }
    }
    Hash1();
    t[0]=Build(1,size);
    for (int i=1;i<=n;i++)
        t[i]=t[0];
    for (int i=1;i<=n;i++)
        Add(i,Hash(v[i]),1);
    for (int i=1;i<=m;i++)
    {
        if (q[i].l)
        {
            printf("%d\n",h[Query(q[i].l,q[i].r,q[i].k)]);
        }
        else
        {
            Add(q[i].r,Hash(v[q[i].r]),-1);
            Add(q[i].r,Hash(q[i].k),1);
            v[q[i].r]=q[i].k;
        }
    }
    return 0;
}

同上 但是带修改操作

时间: 2024-12-12 13:37:46

主席树的相关文章

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树

这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,Lca只是他的一种应用,他可以搞各种树上问题,树上倍增一般都会用到f数组. |||.我们跑出来dfs序就能在他的上面进行主席树了. IV.别忘了离散. V.他可能不连通,我一开始想到了,但是我觉得出题人可能会是好(S)人(B),但是...... #include <cstdio> #include

[bzoj3932][CQOI2015]任务查询系统-题解[主席树][权值线段树]

Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行 ),其优先级为Pi.同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同.调度系统会经常向 查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个 )的优先级之和是多少.特别的,如

BZOJ_3207_花神的嘲讽计划1_(Hash+主席树)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=3207 给出一个长度为\(n\)的串,以及\(m\)个长度为\(k\)的串,求每个长度为\(k\)的串在原串\([x,y]\)区间是否出现过. 分析 这道题要求对比长度为\(k\)的串,于是我们把这些串的Hash值都算出来,问题就转化成了求\([x,y]\)的区间中是否出现过某Hash值. 求区间中某一个值出现了多少次,可以用主席树. p.s. 1.学习了主席树指针的写法,比数组慢好多啊...

[主席树]ZOJ3888 Twelves Monkeys

题意:有n年,其中m年可以乘时光机回到过去,q个询问 下面m行,x,y 表示可以在y年穿越回x年, 保证y>x 下面q个询问, 每个询问有个年份k 问的是k年前面 有多少年可以通过一种以上($\ge 2$)方法穿越回去的, 其中时光机只能用一次 比如案例 9 3 3 9 1 6 1 4 1 6 7 2 如图 对于询问 6这一年:1.穿越回第1年  2.等时间过呀过呀过到第9年,再穿越回第1年 那么第1年就有两种方法可以穿越回去, 同理, 2.3.4年也有同样两种方法(回到1再等时间过呀过 过到2

POJ2104主席树模板题

完成新成就——B站上看了算法https://www.bilibili.com/video/av4619406/?from=search&seid=17909472848554781180#page=2 K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 60158   Accepted: 21054 Case Time Limit: 2000MS Description You are working

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些

(LCA+树上主席树)FZU 2237 - 中位数

题意: 多次查询一个树链上的中位数(其实就是求K大). 分析: 感觉莫队可做,只是不会树上莫队.. 而且这里是边权,处理起来貌似有点小麻烦.. 后来发现其实貌似是一个很老的题,,kuangbin模板书上有类似的题. 树链上的第K大数,这是一道可以用主席树解的题,复杂度才nlogn. 这里也是这样先求从根到每个点的线段树,以为树存在父子关系,所有可以从让下层继承上层的线段树,非常符合主席树的可持久化思想. 然后在查询第K大的时候,去掉重复部分,就可以查了. 太强了,,, 代码: 1 #includ

【主席树】Gym - 101237A - MEX-Query

主席树里每个值的位置存当前该值出现的最右位置. 如果root[r]的前缀主席树中,某值最右位置大于等于l,说明该值出现在了l,r中. 所以主席树维护区间最小值,如果左半值域的最小值<l,则说明左半值域有值未在l,r出现,则查询左子树:否则查询右子树. #include<cstdio> #include<algorithm> using namespace std; #define N 1000010 struct Node{int v,lc,rc;}T[N*22]; int