您的位置:首页 > 理论基础 > 数据结构算法

HDU 4747 线段树+思维

2016-04-30 15:45 399 查看
点击打开链接

题意:给出一个数字序列,定义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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息