hdu 5919 主席树保留区间数的种类数目

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)ki2p⌈ki2⌉(i)for the i-th query.
Input
In the first line of input, there is an integer T (T2T≤2) denoting the number of test cases. 

Each test case starts with two integers n (n2×105n≤2×105) and m (m2×105m≤2×105). There are n integers in the next line, which indicate the integers in the sequence(i.e., a1,a2,,an,0ai2×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 li,ri(1lin,1rin)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 li,rili‘,ri‘)by the following formula: 
li=min{(li+ansi1) mod n+1,(ri+ansi1) mod n+1}li=min{(li‘+ansi−1) mod n+1,(ri‘+ansi−1) mod n+1}

ri=max{(li+ansi1) mod n+1,(ri+ansi1) 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


        
  
Hint
         
  


给你n(n2105)个数,每个数的大小0<Ai2105。再给你m(m2105)个询问。对于每个询问输入l,r,表示Al...Ar这个区间我们得到每个数第一次出现的位置下标的排列,假设这个区间有k个不同的数,我们得到的排列是p1<p2<p3<...<pk,叫你求第(k+1)/2这个数是多少?


首先 要想清楚的是 我们用主席树来记录位置i,而不是值a[i]
从后往前,复制旧的到新的,添加到新的位置,删掉旧的位置,记录 sum数组 代表区间的 值的个数,然后就是一个类似于线段树的操作了





题解:

1、我们利用主席树记录相同的数的前一个位置是多少,很容易得到区间有多少个不同的数,但是我们要得到第(k+1)/2这个数的话我们要二分去求解。时间复杂度为O(nlog2(n)),,在hdu会超时。

2、如果我们从后往前的话在当前位置i我们在主席树上i这个位置加1,在它之前出现的位置减1,然后我们在主席树询问区间的时候每个数都只出现一次了,就不用二分去找那个位置了,时间复杂度退化成O(nlog(n))

#include<cstdio>
#include<cstring>
#include<iostream>
#include<sstream>
#include<algorithm>
#include<vector>
#include<bitset>
#include<set>
#include<queue>
#include<stack>
#include<map>
#include<cstdlib>
#include<cmath>
#define PI 2*asin(1.0)
#define LL long long
#define pb push_back
#define pa pair<int,int>
#define clr(a,b) memset(a,b,sizeof(a))
#define bug(x) printf("%d++++++++++++++++++++%d\n",x,x)
#define key_value ch[ch[root][1]][0]
const int  MOD = 1000000007;
const int N = 2e5 + 15;
const int maxn = 100+ 14;
const int letter = 130;
const int INF = 1e9;
const double pi=acos(-1.0);
const double eps=1e-8;
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

struct node
{
	int lson,rson,sum;
}T[36*N];

int n,q,pre[N],a[N],root[N];
int siz,ps[N];
void insert(int x,int &y,int l,int r,int d,int v){
    y=++siz;
    T[y]=T[x],T[y].sum+=v;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(d<=mid) insert(T[x].lson,T[y].lson,l,mid,d,v);
    else insert(T[x].rson,T[y].rson,mid+1,r,d,v);
}
int query(int x,int l,int r,int ll,int rr){
    if(ll<=l&&r<=rr) return T[x].sum;
    int mid=(l+r)>>1;
    int ans=0;
    if(ll<=mid) ans+=query(T[x].lson,l,mid,ll,rr);
    if(rr>mid)  ans+=query(T[x].rson,mid+1,r,ll,rr);
    return ans;
}
int find_k(int x,int l,int r,int k){
    if(l==r) return l;
    int mid=(l+r)>>1;
    if(T[T[x].lson].sum>=k) return find_k(T[x].lson,l,mid,k);
    else return find_k(T[x].rson,mid+1,r,k-T[T[x].lson].sum);
}
int main(){
    int T,cas=0;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&q);
        clr(pre,0),siz=0;
        clr(root,0);
        for(int i=1;i<=n;i++) scanf("%d",a+i);
        for(int i=n;i;i--){
            insert(root[i+1],root[i],0,n,i,1);
            if(pre[a[i]]) insert(root[i],root[i],0,n,pre[a[i]],-1);
            pre[a[i]]=i;
        }
        ps[0]=0;
        for(int i=1;i<=q;i++){
            int l,r;
            scanf("%d%d",&l,&r);
            l=(l+ps[i-1])%n+1,r=(r+ps[i-1])%n+1;
            if(l>r) swap(l,r);
            int mid=query(root[l],0,n,l,r);
            mid=(mid+1)/2;
            ps[i]=find_k(root[l],0,n,mid);
        }
        printf("Case #%d:",++cas);
        for(int i=1;i<=q;i++) printf(" %d",ps[i]);
        puts("");
    }
    return 0;
}



posted @ 2017-09-19 21:18  黑码的博客  阅读(125)  评论(0编辑  收藏  举报