牛客练习赛34 F little w and Discretization(可持久化线段树)

版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/85242228

猛然发现,过这道题竟然已经是10天前的事情了……

题意大概就是说给你一个序列,每次询问把一个区间的数字离散化,问离散化之后与原本数字不相同的数字个数有多少个。这里有很多个询问,每个询问之间相互独立。

既然是要离散化,我们肯定不能到询问的时候再离散化,肯定是在做所有询问之前,先自己离散化一下。我们考虑一个数字离散化之后与它本身的区别,由于这里说了数列里面的数字都是1~1e9的,所以一个数字离散化之后,肯定是小于等于它本身的。我们要求的是一个区间内,离散化之后与原本数字不相同的个数,我们可以考虑反过来可以求与原本数字相同的个数。对于一个数字,如果在整个区间所有数字离散化情况下为x,那么只离散化一个小区间的时候,数字肯定是小于等于x的。所以说,对于整体离散化之后,如果某个数字已经小于它本身了,那么这个数字就可以不再考虑。

略掉那些直接可以不考虑的数字之后,我们看看剩下的数字怎么处理。观察后可以发现,如果在一个区间内,某一个数字x已经比它离散化之后的数字大了,那么所有大于x的数字一定都会比它对应离散化之后的数字大。那么,我们的问题就转化为了,求一个区间里面最大的一个数字,使得其离散化之后的数字与其本身相同。再一分析,我们可以发现,相当于求一个区间的mex,也即从1开始最大的没有出现过的整数。找到这个mex之后,我们还需要知道这个区间里面有多少个小于等于mex的数字,用区间长度减去这个数字就是我们最后的答案。

那么,现在问题就是如何求区间mex和区间小于等于mex的数字个数。我们考虑用可持久化线段树,对于每一个子线段树,我保存前i个数字离散化后的属性。每一个棵树维护区间[1,n]表示到目前位置每个数字最后晚出现的时间,没有出现则为0,然后维护区间最小值。对于询问(l,r),我在第r棵线段树里面找到一个小于l的最小的位置,这个位置就是我们想要的mex。然后就是求小于等于mex的数字个数,这个也是一样,在可持久化线段树里面多维护一个数字个数即可。

这题在比赛的时候已经想出,但是有一个小地方没有注意到。我们在初始的时候,就可以略去一些一定不会与离散化后数字相等的数字,这些数字不需要加入可持久化线段树,但是线段树的继承信息不能因为这个不加入而断开。这一点我疏忽了,所以GG……具体见代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
 
const int N = 300100;
 
int n,m,num[N],a[N],b[N];
 
int rt[N<<4];                         //空间一定要开大,不要吝啬
 
struct Persistent_SegTree  
{  
    struct node{int l,r,min,sum;} T[N<<5];  
    int cnt; void init(){cnt=0;} 
   
    void ins(int &i,int old,int l,int r,int x,int y)  
    {   
        T[i=++cnt]=T[old]; 
        int mid=(l+r)>>1; T[i].sum++; 
        if (l==r) {T[i].min=y;return;}  
        if (x<=mid) ins(T[i].l,T[old].l,l,mid,x,y);  
               else ins(T[i].r,T[old].r,mid+1,r,x,y);
        T[i].min=min(T[T[i].l].min,T[T[i].r].min);  
    }  
   
    int query(int i,int l,int r,int x)  
    {  
        if (l==r) return l;  
        int mid=(l+r)>>1;  
        if (T[T[i].l].min<x) return query(T[i].l,l,mid,x);  
                    else return query(T[i].r,mid+1,r,x);  
    }  
 
    int getsum(int i,int j,int l,int r,int L,int R)  
    {  
        //cout<<L<<' '<<R<<endl;
        if (l==L&&R==r) return T[j].sum-T[i].sum;  
        int mid=(L+R)>>1;  
        if (r<=mid) return getsum(T[i].l,T[j].l,l,r,L,mid);  
        else if (l>mid) return getsum(T[i].r,T[j].r,l,r,mid+1,R);  
        else return getsum(T[i].l,T[j].l,l,mid,L,mid)+getsum(T[i].r,T[j].r,mid+1,r,mid+1,R);  
    }  
   
} Persist;
 
 
int main()
{
    scanf("%d",&n);
    Persist.init();
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        num[i]=b[i]=a[i];
    }
    sort(num+1,num+1+n);
    int tot=unique(num+1,num+1+n)-num-1;
    for(int i=1;i<=n;i++)
    {
        a[i]=lower_bound(num+1,num+1+tot,a[i])-num;
        if (a[i]==b[i]) Persist.ins(rt[i],rt[i-1],1,tot,a[i],i);
                else rt[i]=rt[i-1];            //这个继承一定不能断开
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        int num=Persist.query(rt[r],1,tot,l);
        //cout<<num<<endl;
        int t=Persist.getsum(rt[l-1],rt[r],1,num,1,tot);
        printf("%d\n",r-l+1-t); 
    }
    return 0;
}

 

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/85242228