HDU 4747 线段树+思维

点击打开链接

题意:给出一个数字序列,定义mex为一段序列中没有出现的最小的自然数,问所有的mex的和

思路:网络赛题目,为何感觉如此的难,对于200000的数列并且有多次的区间和,想是想到要用线段树处理了,但是根本没有接下来的思路了,借鉴神犇的思路神犇的思路来写把,真是不知道人家怎么想的那么一步到位,慢慢学习把~~~,我们首先预处理出来对于第一个数的mex[i],1<=i<=n;然后再将每一个数x的下一个位置Next[i]预处理出来,如果没有下一个位置了,就将下一个位置记为n+1来处理,然后用线段树将mex记录下来,父亲记录区间和,这样num[1]就为每一次的mex的总和,全部加起来就是结果,对于每一次的更新,看例子把,第二组样例,1 0 2 0 1,mex第一次为0 2 3 3 3,然后第一个点去掉后,更新为0 1 1 1 3,然后说一说Next数组的作用,还是这个样例,删掉1后,对于下一个1和下一个1以后的所有mex都不会变,因为没有第一个1,我下一个1也可以代替第一个1的作用,这样后面的所有值都不变,记pos为下一个1之前的位置,变得部分是第二个位置到pos的mex,怎么变呢,找到第一个大于1的mex的位置,也就是第二个位置,到pos的mex更新为1,为什么呢,假如一个位置的mex为2意味着什么,它之前肯定是出现了1,而我们将1删除,他们的值自然都变为1,因为序列的mex肯定是递增的,不用考虑其它情况    PS:第一次写了这长的思路,因为这想法真的太难想了,真心佩服神犇们现场就可以A出来

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=200010;
long long num[maxn*4];
int num1[maxn*4],A[maxn],max1[maxn*4];//num1为懒惰标记,num为和,max1记录区间最大值
int pre[maxn],Next[maxn],mex[maxn],vis[maxn];//Next为下一个数的位置
void pushup(int node){
    num[node]=num[node<<1]+num[node<<1|1];
    max1[node]=max(max1[node<<1],max1[node<<1|1]);
}
void pushdown(int node,int m){
    if(num1[node]){
        num1[node<<1]=num1[node<<1|1]=1;
        num[node<<1]=(ll)max1[node]*(m-(m>>1));
        num[node<<1|1]=(ll)max1[node]*(m>>1);
        max1[node<<1]=max1[node<<1|1]=max1[node];
        num1[node]=0;
    }
}
void buildtree(int le,int ri,int node){
    if(le==ri){
        num[node]=mex[le];
        max1[node]=mex[le];
        return ;
    }
    int t=(le+ri)>>1;
    buildtree(le,t,node<<1);
    buildtree(t+1,ri,node<<1|1);
    pushup(node);
}
void update(int l,int r,int val,int le,int ri,int node){
    if(l<=le&&ri<=r){
        num1[node]=1;
        num[node]=(long long)val*(ri-le+1);
        max1[node]=val;
        return ;
    }
    pushdown(node,ri-le+1);
    int t=(le+ri)>>1;
    if(l<=t) update(l,r,val,le,t,node<<1);
    if(r>t) update(l,r,val,t+1,ri,node<<1|1);
    pushup(node);
}//到这里都是成段更新的模版
int querymax(int val,int le,int ri,int node){//找到区间大于val的第一个位置
    if(le==ri) return le;
    pushdown(node,ri-le+1);
    int ans,t=(le+ri)>>1;
    if(max1[node<<1]>val) ans=querymax(val,le,t,node<<1);
    else ans=querymax(val,t+1,ri,node<<1|1);
    pushup(node);
    return ans;
}
int main(){
    int n;
    while(scanf("%d",&n)!=-1){
        if(n==0) break;
        for(int i=1;i<=n;i++) scanf("%d",&A[i]);
        memset(vis,0,sizeof(vis));
        memset(num1,0,sizeof(num1));
        memset(pre,0,sizeof(pre));
        int k=0;
        for(int i=1;i<=n;i++){
            for(int j=k;j<=n;j++){
                if(vis[j]==0){
                    k=j;break;
                }
            }
            if(A[i]==k){
                for(int j=k+1;j<=n;j++){
                    if(vis[j]==0){
                        k=j;break;
                    }
                }
                mex[i]=k;
            }else mex[i]=k;
            if(A[i]<=n) vis[A[i]]=1;
        }
        for(int i=n;i>=1;i--){
            if(A[i]>n) Next[i]=n+1;
            else if(pre[A[i]]==0){
                pre[A[i]]=i;
                Next[i]=n+1;
            }else{
                Next[i]=pre[A[i]];
                pre[A[i]]=i;
            }
        }
        buildtree(1,n,1);
        long long ans=num[1];
        for(int i=1;i<=n;i++){
            update(i,i,0,1,n,1);
            if(max1[1]>A[i]){
                int pos=querymax(A[i],1,n,1);
                if(pos

你可能感兴趣的:(线段树&树状数组,数据结构,线段树)