Sequence II HDU - 5919

http://acm.hdu.edu.cn/showproblem.php?pid=5919

题意 求给定一个区间 内有k个不同的数 把它们第一次出现的位置升序排序 问第(k+1)/2个数的下标

首先要找出区间内多少不同数 并且没有修改 用静态主席树维护

主席树查询区间不同数一般就是两种方法

法一https://blog.csdn.net/sunyutian1998/article/details/79825659

法二https://blog.csdn.net/sunyutian1998/article/details/81257416

这里用的法一 从前向后扫一边 每个数都更新到最新出现的位置上 也就是这个数目前出现过的下标中最大的一个 体现再对应版本的线段树上就是该下标处值为1 要找第k大的话直接在这个版本的线段树上二分就可以了

注意数组起码要开40倍

 

#include <bits/stdc++.h>
using namespace std;

struct node
{
    int l;
    int r;
    int val;
};

node tree[8000010];
int ary[400010],root[400010],ans[400010],mp[400010];
int n,q,len,num;

int build(int l,int r)
{
    int cur,m;
    cur=num++;
    tree[cur].l=0,tree[cur].r=0,tree[cur].val=0;
    if(l==r) return cur;
    m=(l+r)/2;
    tree[cur].l=build(l,m);
    tree[cur].r=build(m+1,r);
    return cur;
}

int update(int rot,int tar,int val,int l,int r)
{
    int cur,m;
    cur=num++;
    tree[cur]=tree[rot];
    tree[cur].val+=val;
    if(l==r) return cur;
    m=(l+r)/2;
    if(tar<=m) tree[cur].l=update(tree[rot].l,tar,val,l,m);
    else tree[cur].r=update(tree[rot].r,tar,val,m+1,r);
    return cur;
}

int queryI(int rot,int pl,int pr,int l,int r)
{
    int res,m;
    if(pl<=l&&r<=pr) return tree[rot].val;
    res=0,m=(l+r)/2;
    if(pl<=m) res+=queryI(tree[rot].l,pl,pr,l,m);
    if(pr>m) res+=queryI(tree[rot].r,pl,pr,m+1,r);
    return res;
}

int queryII(int rot,int val,int l,int r)
{
    int m;
    if(l==r) return l;
    m=(l+r)/2;
    if(val<=tree[tree[rot].l].val) return queryII(tree[rot].l,val,l,m);
    else return queryII(tree[rot].r,val-tree[tree[rot].l].val,m+1,r);
}

int solve(int rot,int l,int r,int k)
{
    int res;
    if(l>1) res=queryI(rot,1,l-1,1,n);
    else res=0;
    res=queryII(rot,k+res,1,n);
    return res;
}

int main()
{
    int t,cas,i,l,r,k;
    scanf("%d",&t);
    for(cas=1;cas<=t;cas++)
    {
        scanf("%d%d",&n,&q);
        for(i=1;i<=n;i++) scanf("%d",&ary[i]);
        num=0;
        root[n+1]=build(1,n);
        memset(mp,0,sizeof(mp));
        for(i=n;i>=1;i--)
        {
            if(mp[ary[i]]!=0)
            {
                root[i]=update(root[i+1],mp[ary[i]],-1,1,n);
                root[i]=update(root[i],i,1,1,n);
            }
            else
            {
                root[i]=update(root[i+1],i,1,1,n);
            }
            mp[ary[i]]=i;
        }
        for(i=1;i<=q;i++)
        {
            scanf("%d%d",&l,&r);
            l=(l+ans[i-1])%n+1;
            r=(r+ans[i-1])%n+1;
            if(l>r) swap(l,r);
            k=queryI(root[l],l,r,1,n);
            k=(k+1)/2;
            ans[i]=solve(root[l],l,r,k);
        }
        printf("Case #%d:",cas);
        for(i=1;i<=q;i++) printf(" %d",ans[i]);
        printf("\n");
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值