HDU 4747 线段树+思维

来源:互联网 发布:绝世唐门升坐骑数据 编辑:程序博客网 时间:2024/04/25 23:01

点击打开链接

题意:给出一个数字序列,定义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 <stdio.h>#include <stdlib.h>#include <iostream>#include <string.h>#include <algorithm>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<Next[i]) update(pos,Next[i]-1,A[i],1,n,1);            }            ans+=num[1];        }        printf("%I64d\n",ans);    }    return 0;}

0 0
原创粉丝点击