HDU - 5919 Sequence II (主席树,查询区间不同数的个数,查找第 k 大值。)

Mr. Frog has an integer sequence of length n, which can be denoted as a1,a2,⋯,ana1,a2,⋯,anThere are m queries. 

In the i-th query, you are given two integers lili and riri. Consider the subsequence ali,ali+1,ali+2,⋯,ariali,ali+1,ali+2,⋯,ari. 

We can denote the positions(the positions according to the original sequence) where an integer appears first in this subsequence as p(i)1,p(i)2,⋯,p(i)kip1(i),p2(i),⋯,pki(i) (in ascending order, i.e.,p(i)1<p(i)2<⋯<p(i)kip1(i)<p2(i)<⋯<pki(i)). 

Note that kiki is the number of different integers in this subsequence. You should output p(i)⌈ki2⌉p⌈ki2⌉(i)for the i-th query.

Input

In the first line of input, there is an integer T (T≤2T≤2) denoting the number of test cases. 

Each test case starts with two integers n (n≤2×105n≤2×105) and m (m≤2×105m≤2×105). There are n integers in the next line, which indicate the integers in the sequence(i.e., a1,a2,⋯,an,0≤ai≤2×105a1,a2,⋯,an,0≤ai≤2×105). 

There are two integers lili and riri in the following m lines. 

However, Mr. Frog thought that this problem was too young too simple so he became angry. He modified each query to l‘i,r‘i(1≤l‘i≤n,1≤r‘i≤n)li‘,ri‘(1≤li‘≤n,1≤ri‘≤n). As a result, the problem became more exciting. 

We can denote the answers as ans1,ans2,⋯,ansmans1,ans2,⋯,ansm. Note that for each test case ans0=0ans0=0. 

You can get the correct input li,rili,ri from what you read (we denote them as l‘i,r‘ili‘,ri‘)by the following formula: 

li=min{(l‘i+ansi−1) mod n+1,(r‘i+ansi−1) mod n+1}li=min{(li‘+ansi−1) mod n+1,(ri‘+ansi−1) mod n+1}

 

ri=max{(l‘i+ansi−1) mod n+1,(r‘i+ansi−1) mod n+1}ri=max{(li‘+ansi−1) mod n+1,(ri‘+ansi−1) mod n+1}

Output

You should output one single line for each test case. 

For each test case, output one line “Case #x: p1,p2,⋯,pmp1,p2,⋯,pm”, where x is the case number (starting from 1) and p1,p2,⋯,pmp1,p2,⋯,pm is the answer.

Sample Input

2
5 2
3 3 1 5 4
2 2
4 4
5 2
2 5 2 1 2
2 3
2 4

Sample Output

Case #1: 3 3
Case #2: 3 1

思路:

主席树有两个操作,一个查找区间的 不同数的个数,另一个是查找 第k 大的值。

这个题两个都考到了, 

查找区间不同数的个数的时候,需要按照位置插入。好巧的是,这个题要查找 位置的 第 k 大值。

即这个题是按照 位置 插入,并不是数值的大小。 而且是倒着插入位置。

题目要求保存每个数第一次出现的位置,所以我们就可以从后往前插入。

在当前版本的线段树中,如果一个数已经出现,那就在之前出现的位置 -1.。当前位置 + 1.

 

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
struct segtree
{
    int l,r,sum; 
}f[N*36];
int rt[N],cnt,n,m,pre[N],a[N];

void update(int &now, int a, int b, int k, int val){
    f[++cnt] = f[now];
    f[now = cnt].sum += val;
    if (a + 1 == b) return;
    int mid = (a + b) / 2;
    if (k < mid) update(f[now].l,a,mid,k,val); else
    update(f[now].r,mid,b,k,val);
}

int find(int now, int l, int r, int a, int b){
    int ans = 0;
    if (a <= l && b >= r-1){
        return f[now].sum;
    }
    int mid = (l + r) / 2;
    if (a < mid) ans += find(f[now].l,l,mid,a,b);
    if (b >= mid) ans += find(f[now].r,mid,r,a,b);
    return ans;
}

int Query(int now, int l, int r, int k){
    if (l + 1 == r) return l;
    int mid = (l + r) / 2;
    if (f[f[now].l].sum >= k) return Query(f[now].l,l,mid,k);
    return Query(f[now].r,mid,r,k-f[f[now].l].sum);
}
int main(){

    int T,tt=1; scanf("%d",&T);
    while(T--){
        printf("Case #%d:",tt++);
        memset(pre,-1,sizeof(pre));
        memset(f,0,sizeof(f));
        cnt = 0;
        scanf("%d%d",&n,&m);
        for (int i = 1; i <= n; ++i)
            scanf("%d",&a[i]);
        rt[n+1] = 0;
        for (int i = n; i >= 1; --i){
            rt[i] = rt[i+1];
            update(rt[i],1,n+1,i,1);
            if (pre[a[i]] != -1){
                update(rt[i],1,n+1,pre[a[i]],-1);
            }
            pre[a[i]] = i;
        }
        int lr;
        lr = 0;
        for (int i = 0; i < m; ++i){
            int x,y,z,xx,yy; 
            scanf("%d%d",&x,&y);
            xx = min(((x+lr) % n) +1,((y+lr)%n)+1);
            yy = max(((x+lr) % n) +1,((y+lr)%n)+1);
            z = (find(rt[xx],1,n+1,xx,yy) + 1) >> 1;
            lr = Query(rt[xx],1,1+n,z);
            printf(" %d",lr);
        }
        printf("\n");

    }
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值