HDU-5919 Sequence II 主席树二分

Sequence II

题意:给你n个数,q次查询,每 次查询一个区间,求这个区间的不同数的最中间的那个的第一次出现的位置。

主席树裸题吧。查询区间不同数的个数模板然后嵌套个二分,二分答案mid,判断[l,mid]之间不同数的个数是否大于等于(k+1)/2,是则更新右端点,反之更新左端点,注意强制在线的左端点不能变,另外用一个变量表示左端点,每次更新它即可。 

const int N=2e5+10;
inline int sc()
{
    char ch=getchar();
    int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=getchar();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=getchar();
    return sum;
}
int n,q,tot;
int a[N],T[N],lc[N*50],rc[N*50],c[N*50];
int ans[N];
int build(int l,int r)
{
    int root=tot++;
    c[root]=0;
    if(l!=r)
    {
        int mid=(l+r)>>1;
        lc[root]=build(l,mid);
        rc[root]=build(mid+1,r);
    }
    return root;
}
int update(int root,int pos,int val)
{
    int newroot=tot++,tmp=newroot;
    c[newroot]=c[root]+val;
    int l=1,r=n;
    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 query(int root,int pos)
{
    int ret=0;
    int l=1,r=n;
    while(pos<r)
    {
        int mid=(l+r)>>1;
        if(pos<=mid)
        {
            r=mid;
            root=lc[root];
        }
        else
        {
            ret+=c[lc[root]];
            root=rc[root];
            l=mid+1;
        }
    }
    return ret+c[root];
}
map<int,int>mp;
int main()
{
    int t;
    scanf("%d",&t);
    int t1=t;
    while(t--)
    {
        tot=0;
        scanf("%d%d",&n,&q);
        for(int i=1; i<=n; i++) a[i]=sc();
        T[n+1]=build(1,n);
        mp.clear();
        for(int i=n; i>=1; i--)
        {
            if(mp.find(a[i])==mp.end()) T[i]=update(T[i+1],i,1);
            else
            {
                int tmp=update(T[i+1],mp[a[i]],-1);
                T[i]=update(tmp,i,1);
            }
            mp[a[i]]=i;
        }
        int l,r;
        ans[0]=0;
        for(int i=1; i<=q; i++)
        {
            l=sc();
            r=sc();
            int l1,r1;
            l1=min((l+ans[i-1])%n+1,(r+ans[i-1])%n+1);
            r1=max((l+ans[i-1])%n+1,(r+ans[i-1])%n+1);
            l=l1,r=r1;
            int k=(1+query(T[l1],r1))/2;
            while(l1<=r1)
            {
                int mid=(l1+r1)>>1;
                if(query(T[l],mid)>=k) ans[i]=mid,r1=mid-1;
                else l1=mid+1;
            }
        }
        printf("Case #%d: ",t1-t);
        for(int i=1; i<=q; i++) printf("%d%c",ans[i],i==q?'\n':' ');
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值