• 【线段树】【积累】主席树杂题积累 2016CCPC长春K SequenceII


    主席树杂题积累 2016CCPC长春K SequenceII

    2016CCPC长春K SequencII

    题意

    给定 (n,m) ,一个长度为 (n) 的数组 (a)(m) 个询问。每个询问给出 (l,r) 要求输出第 (lceil{frac{k}{2}} ceil) 个在区间 ([l,r]) 第一次出现的元素的位置。 其中 (k) 是区间 ([l,r]) 中,不同元素的个数。

    (1leq lleq rleq nleq 2 imes10^5)(0leq a_i,mleq2 imes 10^5)

    (T) 的测试样例,(Tleq2)

    解:

    建主席树,这里的主席树是指静态求第k小的那种主席树,即动态开点权值线段树。

    (n)(1) 建主席树。以下标为权值线段树的权值区间。

    若对于当前的点 (i)(a_i) 在此之前没有出现过(即 (forall\,a_j[j>i],a_i e a_j) ),则对当前点的线段树,权值位置 (i) 的值 (+1) ,否则,记 (a_i) 在此前最后一次出现的位置为 (last_{a_i}),另建一个棵树 ((Tmp)) (这里的树均指主席树内里的树),这棵新树以 (T[i+1])(pre) ,并对其权值位置 (last_{a_i}) 的值 (-1) ,再对点 (i) 建一棵树( (T[i]) ),这颗树以 (Tmp)(pre) ,权值位置 (i) 的值 (+1)

    在这样的建立下,可以发现,对于 (T[i]),其权值区间,每一个有值的点都是在 (i) 之后其对应的 (a) 值第一次出现的点。

    (T[l]) 的权值区间 ([l,r]) 的和对应数组 (a) 在区间 ([l,r]) 不同元素的个数。

    再用求第k小的做法即可求对应第 (lceilfrac kn ceil) 个元素的位置。

    叨叨:

    在这个思路里,数组 (a) 的权值并没有起到什么作用,只是需要判断每个位置上的元素是否出现过,以及对于当前点记录之后元素第一次出现的位置

    这种思路我没有想到;很好的一道题。

    代码:

    #include<bits/stdc++.h>
    typedef long long ll;
    using namespace std;
    const int maxn=5e5+5;
    const int mod=1e9+7;
    
    int a[maxn],last[maxn];
    int T[maxn],L[maxn<<4],R[maxn<<4],tnt,sum[maxn<<4];
    void update(int&rt,int pre,int l,int r,int x,int v)
    {
        rt=++tnt;
        sum[rt]=sum[pre]+v;L[rt]=L[pre],R[rt]=R[pre];
        if(l==r)return;
        int mid=(l+r)>>1;
        if(x<=mid)update(L[rt],L[pre],l,mid,x,v);
        else update(R[rt],R[pre],mid+1,r,x,v);
    }
    int query(int rt,int l,int r,int ql,int qr)
    {
        if(ql>r||qr<l)return 0;
        if(ql<=l&&r<=qr)return sum[rt];
        int mid=(l+r)>>1;
        return query(L[rt],l,mid,ql,qr)+query(R[rt],mid+1,r,ql,qr);
    }
    int query1(int rt,int l,int r,int k)
    {
        if(l==r)return l;
        int mid=(l+r)>>1;
        if(sum[L[rt]]>=k)return query1(L[rt],l,mid,k);
        else return query1(R[rt],mid+1,r,k-sum[L[rt]]);
    }
    int res[maxn];
    int main()
    {
        int TT,cas=0;
        scanf("%d",&TT);
        while(TT--)
        {
            int n,m;
            scanf("%d%d",&n,&m);
            memset(last,0,sizeof last);
            memset(T,0,sizeof T);tnt=0;
            memset(L,0,sizeof L);
            memset(R,0,sizeof R);
            memset(sum,0,sizeof sum);
            for(int i=1;i<=n;i++)scanf("%d",&a[i]);
            for(int i=n;i>=1;i--)
            {
                if(!last[a[i]])update(T[i],T[i+1],1,n,i,1);
                else {
                    int tmp;
                    update(tmp,T[i+1],1,n,last[a[i]],-1);
                    update(T[i],tmp,1,n,i,1);
                }
                last[a[i]]=i;
            }
            int l,r,k,st,mid,p=0,tk;
            int mm=0;
            while(m--)
            {
                scanf("%d%d",&l,&r);
                l=(l+p)%n+1;
                r=(r+p)%n+1;
                if(l>r)swap(l,r);
                k=query(T[l],1,n,l,r);
                tk=(k+1)>>1;
                p=query1(T[l],1,n,tk);
    //            printf("%d %d
    ",k,p);
                res[++mm]=p;
            }
            printf("Case #%d: ",++cas);
            for(int i=1;i<=mm;i++)printf("%d%c",res[i]," 
    "[i==mm]);
        }
    }
    
  • 相关阅读:
    Centeos7搭建selenium+Chrome浏览器
    数据结构学习篇之栈和队列
    数据结构学习篇之线性表
    Tornado基础学习篇
    Python控制函数运行时间
    python线程实现异步任务
    Python实现几种简单的排序算法
    python爬虫遇到会话存储sessionStorage
    Python 有哪些优雅的代码实现让自己的代码更pythonic?
    Ubuntu查看端口使用情况,使用netstat命令:
  • 原文地址:https://www.cnblogs.com/kkkek/p/13861124.html
Copyright © 2020-2023  润新知