点击打开链接
题意:给出一个数字序列,定义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; }