HDU4747 2013 ACM/ICPC Asia Regional Hangzhou Online Mex(线段树)

手算可以发现,当一组数列a[x],去掉a[0]后,那么之后新的数列数列的mex值将会发生变化

对于第二组样例 1 0 2 0 1

我们一步步算

得到mex(i,j)(j>=i)

0+2+3+3+3 = 11 //第一次可以得到mex(1,i)(1<=i<=n)

     1+1+1+3 = 6   

          0+1+3 = 4

               1+2 = 3

                    0 = 0 sum=24

每次删除a[i]时,mex的值只影响到nextpos[a[i]],nextpos表示a[i]下一次出现的值的下标,也就是对于区间[i+1,nextpos[a[i]]中的大于a[i]数字改为a[i]。区间更新可以用线段树来维护。这样就将二维的矩阵mex化为一维处理了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
#define maxn 200005
#define LL long long
int a[maxn];
int num[maxn];
int p[maxn];
bool vis[maxn];
vector<int>pos[maxn];
vector<int>::iterator it;
set<int>lst;
set<int>::iterator it1;
struct node
{
    int v,i;
    bool operator < (const node& tmp) const
    {
        return v<tmp.v;
    }
} b[maxn];
int n;
struct SegTree
{
    int l,r;
    int mi,ma;
    LL sum;
    int flag;
    int len()
    {
        return r-l+1;
    }
} tree[maxn<<2];
inline void pushup(int rt)
{
    int ls=rt<<1,rs=ls+1;
    tree[rt].sum=tree[ls].sum+tree[rs].sum;
    tree[rt].mi=min(tree[ls].mi,tree[rs].mi);
    tree[rt].ma=max(tree[ls].ma,tree[rs].ma);
}
inline void pushdown(int rt)
{
    if(tree[rt].flag)
    {
        int ls=rt<<1,rs=rt<<1|1;
        tree[ls].flag=tree[rs].flag=1;
        tree[ls].mi=tree[rs].mi=tree[ls].ma=tree[rs].ma=tree[rt].mi;
        tree[ls].sum=tree[ls].mi*tree[ls].len();
        tree[rs].sum=tree[rs].mi*tree[rs].len();
        tree[rt].flag=0;
    }
}
void build(int rt,int l,int r)
{
    tree[rt].l=l,tree[rt].r=r;
    tree[rt].flag=0;
    if(l==r)
    {
        tree[rt].mi=tree[rt].ma=tree[rt].sum=p[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    pushup(rt);
}
void update(int rt,int l,int r,int k)
{
    if(r<l) return ;
    if(tree[rt].l>=l&&tree[rt].r<=r&&tree[rt].mi>k)
    {
        tree[rt].mi=tree[rt].ma=k;
        tree[rt].flag=1;
        tree[rt].sum=k*tree[rt].len();
        return ;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    pushdown(rt);
    int ls=rt<<1,rs=rt<<1|1;
    if(l<=mid&&tree[ls].ma>k) update(ls,l,r,k);
    if(r>mid&&tree[rs].ma>k) update(rs,l,r,k);
    pushup(rt);
}
int main()
{
    while(~scanf("%d",&n))
    {
        if(!n) break;
        lst.clear();
        memset(vis,0,sizeof(vis));
        for(int i=0; i<=n; i++)
            lst.insert(i);
        for(int i=1; i<=n; i++)
        {
            scanf("%d",a+i);
            b[i].v=a[i];
            b[i].i=i;
        }
        sort(b+1,b+1+n);
        int cnt=0;
        for(int i=1; i<=n; i++)
        {
            if(i>1&&b[i].v==b[i-1].v) a[b[i].i]=cnt;
            else a[b[i].i]=++cnt,num[cnt]=b[i].v;
        }
        for(int i=1; i<=cnt; i++) pos[i].clear();
        for(int i=1; i<=n; i++) pos[a[i]].push_back(i);
        for(int i=1; i<=cnt; i++) pos[i].push_back(n+1);
//        for(int i=1; i<=cnt; i++)
//        {
//            printf("num'pos: %d\n",num[i]);
//            for(it=pos[i].begin(); it!=pos[i].end(); it++)
//                printf("%d ",*it);
//            puts("");
//        }
        for(int i=1; i<=n; i++)
        {
            int t=num[a[i]];
            if(t<=n&&!vis[t])
            {
                it1=lst.find(t);
                lst.erase(it1);
                vis[t]=1;
            }
            it1=lst.begin();
            p[i]=*it1;
        }
//        for(int i=1; i<=n; i++) printf("%d ",p[i]);
//        puts("");
        build(1,1,n);
        LL ans=tree[1].sum;
        for(int i=1; i<n; i++)
        {
            pos[a[i]].erase(pos[a[i]].begin());
            int nxt=*(pos[a[i]].begin());
            update(1,1,i,0);
            update(1,i+1,nxt-1,num[a[i]]);
            ans+=tree[1].sum;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值