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

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

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

Note that  ki ki 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 ( T2 T≤2) denoting the number of test cases. 

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

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

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

You can get the correct input  li,ri li,ri from what you read (we denote them as  li,ri li‘,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,,pm p1,p2,⋯,pm”, where x is the case number (starting from 1) and  p1,p2,,pm 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(
   
   
    
    n2105
   
   )个数,每个数的大小
   
   
    
    0<Ai2105
   
   。再给你m(m
   
   
    
    2105
   
   )个询问。对于每个询问输入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;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值