zoukankan      html  css  js  c++  java
  • hdu 5919--Sequence II(主席树--求区间不同数个数+区间第k大)

    题目链接

    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
    */
  • 相关阅读:
    消息队列接口API(posix 接口和 system v接口)
    Ubuntu 安装 Eclipse C/C++开发环境
    Ubuntu下Eclipse搭建ARM开发环境
    Linux进程间通信——使用流套接字
    Linux进程间通信——使用数据报套接字
    Linux进程间通信——信号集函数
    Linux进程间通信——使用信号
    Linux进程间通信——使用匿名管道
    mappedBy的作用
    VS Code 配置 C/C++ 环境
  • 原文地址:https://www.cnblogs.com/chen9510/p/7651538.html
Copyright © 2011-2022 走看看