• 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))


    函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同…

    那么原理也比较易懂:
    建造一棵线段树(权值线段树),维护的信息是序列中每个数的出现次数,静态查询第K极值,只需要从根做二分,然后向下转左右子树,找到叶子节点即可…(由于线段树的性质,这个查找的复杂度是log级..)

    那么动态的第K极值呢..
    需要用上树状数组,这时树状数组维护的其实就是一串主席树了,不过这样处理,会MLE,但是应用可持久化原理,每次修改,对于线段树来说,只是修改了一个叶子点到根的路径,其余的路径都是不变的,所以每次只需要保存一条路径即可咯,但是如何找到那样的数 呢….此处应用前缀和操作,r和l-1的差值即为l~r的值,所以用同样原理,应用树状数组维护即可..代码量会降低..

    动态维护模板(ZOJ2112)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define MAXN 60010
    #define M 2500010
    int n,q,m,tot;
    int a[MAXN],t[MAXN];
    int T[MAXN],lc[M],rc[M],c[M];
    int S[MAXN];
    struct data{
        int kind;int l,r,k;
    }query[10010];
    
    void init_hash(int num)
    {
        sort(t,t+num);
        m=unique(t,t+num)-t;
    }
    
    int hash(int x)
    {
        return lower_bound(t,t+m,x)-t;
    }
    
    int build(int l,int r)
    {
        int root=tot++;
        c[root]=0;
        if (l!=r)
            {
                int mid=(l+r)/2;
                lc[root]=build(l,mid);
                rc[root]=build(mid+1,r);
            }
        return root;
    }
    
    int insert(int root,int pos,int val)
    {
        int newroot=tot++,tmp=newroot;
        int l=0,r=m-1;
        c[newroot]=c[root]+val;
        while (l<r)
            {
                int mid=(l+r)>>1;
                if (pos<=mid)
                    {
                        lc[newroot]=tot++; rc[newroot]=rc[root];
                        newroot=lc[newroot]; root=lc[root];
                        r=mid;
                    }
                else
                    {
                        rc[newroot]=tot++; lc[newroot]=lc[root];
                        newroot=rc[newroot]; root=rc[root];
                        l=mid+1;
                    }
                c[newroot]=c[root]+val;
            }
        return tmp;
    }
    
    int lowbit(int x){return x&(-x);}
    
    int use[MAXN];
    void add(int x,int pos,int val)
    {
        while (x<=n)
            {
                S[x]=insert(S[x],pos,val);
                x+=lowbit(x);
            }
    }
    
    int sum(int x)
    {
        int re=0;
        while (x>0)
            {
                re+=c[lc[use[x]]];
                x-=lowbit(x);
            }   
        return re;
    }
    
    int Query(int L,int R,int k)
    {
        int l_root=T[L-1];
        int r_root=T[R];
        int l=0,r=m-1;
        for (int i=L-1; i; i-=lowbit(i)) use[i]=S[i];
        for (int i=R; i; i-=lowbit(i)) use[i]=S[i];
        while (l<r)
            {
                int mid=(l+r)>>1;
                int tmp=sum(R)-sum(L-1)+c[lc[r_root]]-c[lc[l_root]];
                if (tmp>=k)
                    {
                        r=mid;
                        for (int i=L-1; i; i-=lowbit(i)) use[i]=lc[use[i]];
                        for (int i=R; i; i-=lowbit(i)) use[i]=lc[use[i]];
                        l_root=lc[l_root];
                        r_root=lc[r_root];
                    }
                else
                    {
                        l=mid+1;k-=tmp;
                        for (int i=L-1; i; i-=lowbit(i)) use[i]=rc[use[i]];
                        for (int i=R; i; i-=lowbit(i)) use[i]=rc[use[i]];
                        l_root=rc[l_root];
                        r_root=rc[r_root];
                    }
            }
        return l;
    }
    
    void Modify(int x,int p,int d)
    {
        while (x<=n)
            {
                S[x]=insert(S[x],p,d);
                x+=lowbit(x);
            }
    }
    int main()
    {
        int test;
        scanf("%d",&test);
        while (test--)
            {
                scanf("%d%d",&n,&q);
                tot=0;m=0;
                for (int i=1; i<=n; i++)
                    {
                        scanf("%d",&a[i]);
                        t[m++]=a[i];
                    }
                char opt[10];
                for (int i=0; i<q; i++)
                    {
                        scanf("%s",opt);
                        if (opt[0]=='Q')
                            {
                                query[i].kind=0;
                                scanf("%d%d%d",&query[i].l,&query[i].r,&query[i].k);
                            }
                        else
                            {
                                query[i].kind=1;
                                scanf("%d%d",&query[i].l,&query[i].r);
                                t[m++]=query[i].r;
                            }
                    }
                init_hash(m);
                T[0]=build(0,m-1);
                for (int i=1; i<=n; i++)
                    T[i]=insert(T[i-1],hash(a[i]),1);
                for (int i=1; i<=n; i++)
                    S[i]=T[0];
                for (int i=0; i<q; i++)
                    {
                        if (query[i].kind==0)
                            printf("%d
    ",t[Query(query[i].l,query[i].r,query[i].k)]);
                        else
                            {
                                Modify(query[i].l,hash(a[query[i].l]),-1);
                                Modify(query[i].l,hash(query[i].r),1);
                                a[query[i].l]=query[i].r;
                            }
                    }
            }
        return 0;
    }
  • 相关阅读:
    (二十八)缓存:很多时候我们都用错了!
    (二十七)缓存:进程内缓存要怎么玩?
    JavaScript 获取7天之前或之后的日期
    实现文本复制功能
    vue项目 PC端点击查看大图
    vue使用canvas生成海报图
    禁用微信转发给好友和朋友圈
    vue防抖节流函数---组件封装,防止按钮多次点击
    看到几个不错的打印方式,分享几个觉得不错的
    为啥没更新呢
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5346210.html
Copyright © 2020-2023  润新知