1. 程式人生 > >hdu 5919--Sequence II(主席樹--求區間不同數個數+區間第k大)

hdu 5919--Sequence II(主席樹--求區間不同數個數+區間第k大)

positions minus -s ima date rst itl 主席樹 技術

題目鏈接

Problem Description Mr. Frog has an integer sequence of length n, which can be denoted as a1,a2,?,an There are m queries.

In the i-th query, you are given two integers li and ri. Consider the subsequence ali,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)ki
(in ascending order, i.e.,p(i)1<p(i)2<?<p(i)ki).

Note that ki is the number of different integers in this subsequence. You should output p(i)ki2for the i-th query.

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

Each test case starts with two integers n (n2×105
) and m (m2×105). There are n integers in the next line, which indicate the integers in the sequence(i.e., a1,a2,?,an,0ai2×105).

There are two integers li and ri 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 li,ri(1lin,1rin)
. As a result, the problem became more exciting.

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

You can get the correct input li,ri from what you read (we denote them as li,ri)by the following formula:
li=min{(li+ansi1) mod n+1,(ri+ansi1) mod n+1}
ri=max{(li+ansi1) mod n+1,(ri+ansi1) 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,?,pm”, where x is the case number (starting from 1) and p1,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 Hint 技術分享 題意:一個有n個數的序列,現在有m次詢問,每次給一個區間(l,r),設區間中有k個不同的數,它們在區間中第一次出現的位置為p1,p2 ,……,pk 並且將它們排序p1<p2<……<pk,現在求p[(k+1)/2]的值? 思路:主席樹記錄當前數出現的位置,即每次在當前數出現的位置(下標)+1,對於序列數a[1]~a[n]建立主席樹時,如果當前這個數之前出現過,那麽在當前這個版本線段樹上對前一次出現的位置-1,在當前位置+1,這樣就可以求出區間不同數的個數。現在要求出區間第k大,那麽可以從a[n]~a[1]建立線段樹,然後直接求k大就行。 代碼如下:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;
typedef long long LL;
const int N=2e5+5;
int a[N],ans[N];
int t[N],tot;
map<int,int>mp;
struct Node
{
    int l,r;
    int num;
}tr[40*N];
void init()
{
    tot=0;
    mp.clear();
}
int build(int l,int r)
{
    int ii=tot++;
    tr[ii].num=0;
    if(l<r)
    {
        int mid=(l+r)>>1;
        tr[ii].l=build(l,mid);
        tr[ii].r=build(mid+1,r);
    }
    return ii;
}
int update(int now,int l,int r,int x,int y)
{
    int ii=tot++;
    tr[ii].num=tr[now].num+y;
    tr[ii].l=tr[now].l;
    tr[ii].r=tr[now].r;
    if(l<r)
    {
        int mid=(l+r)>>1;
        if(x<=mid) tr[ii].l=update(tr[now].l,l,mid,x,y);
        else       tr[ii].r=update(tr[now].r,mid+1,r,x,y);
    }
    return ii;
}
int query(int now,int l,int r,int L,int R)
{
    if(L<=l&&r<=R) return tr[now].num;
    int mid=(l+r)>>1;
    int sum=0;
    if(mid>=L) sum+=query(tr[now].l,l  ,mid,L,R);
    if(mid<R)  sum+=query(tr[now].r,mid+1,r,L,R);
    return sum;
}
int finds(int now,int l,int r,int k)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    if(tr[tr[now].l].num>=k) return finds(tr[now].l,l,mid,k);
    else return finds(tr[now].r,mid+1,r,k-tr[tr[now].l].num);
}
int main()
{
    int T,Case=1; cin>>T;
    while(T--)
    {
        init();
        int n,m; scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        t[n+1]=build(1,n);
        for(int i=n;i>=1;i--)
        {
            if(mp.count(a[i]))
            {
                int tmp=update(t[i+1],1,n,mp[a[i]],-1);
                t[i]=update(tmp,1,n,i,1);
            }
            else t[i]=update(t[i+1],1,n,i,1);
            mp[a[i]]=i;
        }
        ans[0]=0;
        for(int i=1;i<=m;i++)
        {
            int x,y; scanf("%d%d",&x,&y);
            int l=min((x+ans[i-1])%n+1,(y+ans[i-1])%n+1);
            int r=max((x+ans[i-1])%n+1,(y+ans[i-1])%n+1);
            int k=(query(t[l],1,n,l,r)+1)/2;
            ans[i]=finds(t[l],1,n,k);
        }
        printf("Case #%d:",Case++);
        for(int i=1;i<=m;i++) printf(" %d",ans[i]);
        puts("");
    }
    return 0;
}
/**
10 4
1 1 1 1 1 1 1 1 1 1
3 6
6 8
7 10
2 5
*/

hdu 5919--Sequence II(主席樹--求區間不同數個數+區間第k大)