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

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

2016CCPC长春K SequencII

题意

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

\(1\leq l\leq r\leq n\leq 2\times10^5\)\(0\leq a_i,m\leq2\times 10^5\)

\(T\) 的测试样例,\(T\leq2\)

解:

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

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

若对于当前的点 \(i\)\(a_i\) 在此之前没有出现过(即 \(\forall\,a_j[j>i],a_i\ne 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小的做法即可求对应第 \(\lceil\frac kn\rceil\) 个元素的位置。

叨叨:

在这个思路里,数组 \(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\n",k,p);
            res[++mm]=p;
        }
        printf("Case #%d: ",++cas);
        for(int i=1;i<=mm;i++)printf("%d%c",res[i]," \n"[i==mm]);
    }
}
posted @ 2020-10-22 21:26  草丛怪  阅读(110)  评论(0编辑  收藏  举报