hdu5919 Sequence II(主席树,区间第k大)

本文介绍了解决HDU5919问题的方法,该问题涉及一系列数字和查询操作,目标是通过主席树来找出特定条件下的数值。文章详细展示了如何利用主席树保存数字首次出现的位置,并通过代码实现了解题思路。

hdu5919

题目

给你n个数字,和m个查询.将[l,r]之间数第一次出现的位置信息弄成一个新的数组,然后找出其中k/2大的数.(k为位置的数量)

思路

主席树,倒着插入可以保证存的是数字第一次出现的位置。强行学习了一波。(借鉴别人的代码)

代码

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N=2e5+5;
const int M=40*N;
int T[N],po[N],a[N];
struct Seg
{
    int ls,rs,v;
} tr[M];
int tot,C,n,m;

int build(int l,int r)
{
    int rt=++tot;
    tr[rt].v=0;
    if(l==r)
    {
        tr[rt].ls=tr[rt].rs=0;
        return rt;
    }
    int m=(l+r)>>1;
    tr[rt].ls=build(l,m);
    tr[rt].rs=build(m+1,r);
    return rt;
}

int update(int rt,int l,int r,int pos,int d)
{
    int newrt=tot++;
    tr[newrt].v=tr[rt].v+d;
    if(l==r) return newrt;
    int m=(l+r)>>1;
    if(pos<=m)
    {
        tr[newrt].rs=tr[rt].rs;
        tr[newrt].ls=update(tr[rt].ls,l,m,pos,d);
    }
    else
    {
        tr[newrt].ls=tr[rt].ls;
        tr[newrt].rs=update(tr[rt].rs,m+1,r,pos,d);
    }
    return newrt;
}

int getsum(int rt,int l,int r,int L,int R)
{
    if(L<=l&&r<=R) return tr[rt].v;
    int m=(l+r)>>1;
    int res=0;
    if(L<=m) res+=getsum(tr[rt].ls,l,m,L,R);
    if(m<R) res+=getsum(tr[rt].rs,m+1,r,L,R);
    return res;
}

int query(int rt,int l,int r,int k)
{
    if(l==r) return l;
    int m=(l+r)>>1;
    int tmp=tr[tr[rt].ls].v;
    if(k<=tmp) return query(tr[rt].ls,l,m,k);
    else return query(tr[rt].rs,m+1,r,k-tmp);
}

int main()
{
    int t,kase=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %d",&n,&m);
        tot=0;
        T[n+1]=build(1,n);
        memset(po,-1,sizeof(po));
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        for(int i=n; i>=1; i--)
        {
            if(po[a[i]]==-1)
            {
                T[i]=update(T[i+1],1,n,i,1);
            }
            else
            {
                int tmp=update(T[i+1],1,n,po[a[i]],-1);
                T[i]=update(tmp,1,n,i,1);
            }
            po[a[i]]=i;
        }
        printf("Case #%d:",kase++);
        int ans=0;
        while (m--)
        {
            int p,q,l,r;
            scanf("%d %d",&p,&q);
            p=(p+ans)%n+1;
            q=(q+ans)%n+1;
            l=min(p,q);
            r=max(p,q);
            int k=getsum(T[l],1,n,l,r);
            ans=query(T[l],1,n,(k+1)/2);
            printf(" %d",ans);
        }
        puts("");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值