hdu 4747 Mex

来源:互联网 发布:汉文化复兴 知乎 编辑:程序博客网 时间:2024/06/10 22:15

题意:求所有子区间Mex的和。Mex是最小的,不存在集合里的非负整数

思路:

首先考虑从1开始的所以Mex值,必然是非递减的,并且可以O(n)求出。(具体看代码)

先考虑如何从1开始的Mex值求出从2开始的Mex值

1.如果Mex<a[1],那么去掉后Mex值不变

2.如果Mex>a[1],并且该区间内不包含另外一个a[x]==a[1],那么Mex=a[1]。

那么我们需要先预处理出next数组,保存下一个a[i]的位置,那么每次把这样一段区间内的大于a[i]的值置为a[i]即可,由于Mex单调,那么我们只要找到位置最靠前的并且大于a[i]的,然后把改位置以后,下一个a[i]的位置之前的值全部置为a[i]即可。

那么我们可以用线段树来维护修改,查询的操作。

#include <iostream>#include <cstdio>#include <cstring>#define ls (t<<1)#define rs (t<<1|1)#define midt (tr[t].l+tr[t].r>>1)using namespace std;const int maxn=2e5+9;int a[maxn],now[maxn],next[maxn],first[maxn];bool flag[maxn];int n;struct{    int l,r,max;    long long sum,lazy;}tr[maxn<<2];void pushup(int t){    tr[t].sum=tr[ls].sum+tr[rs].sum;    tr[t].max=max(tr[ls].max,tr[rs].max);}void maketree(int t,int l,int r){    tr[t].l=l;    tr[t].r=r;    tr[t].lazy=-1;    if(l==r)    {        tr[t].sum=now[l];        tr[t].max=now[l];        return ;    }    maketree(ls,l,midt);    maketree(rs,midt+1,r);    pushup(t);}void pushdown(int t){    if(tr[t].lazy==-1) return;    tr[ls].sum=(tr[ls].r-tr[ls].l+1)*tr[t].lazy;    tr[rs].sum=(tr[rs].r-tr[rs].l+1)*tr[t].lazy;    tr[ls].max=tr[rs].max=tr[t].lazy;    tr[ls].lazy=tr[rs].lazy=tr[t].lazy;    tr[t].lazy=-1;}int query(int t,int tmp){    if(tr[t].max<tmp) return n+1;    if(tr[t].l==tr[t].r) return tr[t].l;    pushdown(t);    if(tr[ls].max>=tmp) return query(ls,tmp);    return query(rs,tmp);}void modify(int t,int l,int r,long long tmp){    if(tr[t].l==l&&tr[t].r==r)    {        tr[t].sum=tmp*(r-l+1);        tr[t].lazy=tr[t].max=tmp;        return ;    }    pushdown(t);    if(r<=midt) modify(ls,l,r,tmp);    else if(midt+1<=l) modify(rs,l,r,tmp);    else    {        modify(ls,l,midt,tmp);        modify(rs,midt+1,r,tmp);    }    pushup(t);}int main(){//    freopen("in.txt","r",stdin);    while(scanf("%d",&n),n)    {        for(int i=1;i<=n;i++)        scanf("%d",&a[i]);        memset(flag,0,sizeof(flag));        for(int i=1,top=0;i<=n;i++)        {            if(a[i]<=n)            flag[a[i]]=1;            while(flag[top]) top++;            now[i]=top;        }                for(int i=n;i>=0;i--) first[i]=n+1;        for(int i=n;i>=1;i--)        if(a[i]<=n)        {            next[i]=first[a[i]];            first[a[i]]=i;        }        else next[i]=n+1;                maketree(1,1,n);        long long ans=tr[1].sum;        for(int i=1;i<n;i++)        {            int ll=query(1,a[i]);            if(ll<=next[i]-1)            modify(1,ll,next[i]-1,a[i]);            ans+=tr[1].sum;        }        cout<<ans<<endl;    }    return 0;}



原创粉丝点击