JZOJ.4680. 【NOIP2016提高A组8.11】自然数

Problem

Description

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Sample Input

3
0 1 3

Sample Output

5
这里写图片描述

Data Constraint

这里写图片描述

Solution

我们不要看到 0ai109 就怕,其实开数组就开一个200000的就OK了。
首先我们求出mex(1,i)。我们可以发现,mex(1,i)是单调递增的。
怎么求mex(1,i)?
设序列数组为c,对于每一个读入的 ci ,只需要处理≤n的 ci (想一想为什么)。
然后开next数组来方便求出下一个c[i]的位置。

fo(i,1,n)
{
    if (c[i]<=n)
    {
        next[bz[c[i]]]=i;
        bz[c[i]]=i;
    }
    while (bz[p]) p++;
}

然后再求mex(2,i),mex(3,i)….每一次删除掉一个数x,那么在下一个x出现前的>x的mex值都要变成x。
我们利用mex数列的单调递增性,每次找到第一个mex>x的位置,记为wz,那么将wz~next[i]的所有mex值全部变成x。

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define N 200010
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int bz[N],next[N];
LL tree[N*10][3];
int i,j,k,n,m,p,bj;
long long ans,c[N],_1[N];
void build(int s,int l,int r)
{
    tree[s][0]=-1;
    if (l==n) bj=s;
    if (l==r)
    {
        tree[s][1]=tree[s][2]=_1[l];
        return;
    }
    int wz=(l+r)/2;
    build(s*2,l,wz);
    build(s*2+1,wz+1,r);
    tree[s][1]=max(tree[s*2][1],tree[s*2+1][1]);
    tree[s][2]=tree[s*2][2]+tree[s*2+1][2];
}
int find(int s,int l,int r,int x)
{
    if (l==r) return l;
    int wz=(l+r)/2;
    if (tree[s][0]!=-1)
    {
        tree[s*2][0]=tree[s*2][1]=tree[s][0];
        tree[s*2+1][0]=tree[s*2+1][1]=tree[s][0];
        tree[s*2][2]=(wz-l+1)*tree[s][0];
        tree[s*2+1][2]=(r-wz)*tree[s][0];
        tree[s][0]=-1;
    }
    if (tree[s*2][1]>x) return find(s*2,l,wz,x);
              else return find(s*2+1,wz+1,r,x);
    tree[s][1]=max(tree[s*2][1],tree[s*2+1][1]);
    tree[s][2]=tree[s*2][2]+tree[s*2+1][2];
}
void change(int s,int l,int r,int x,int y,LL z)
{
    if (l==x && r==y)
    {
        tree[s][0]=tree[s][1]=z;
        tree[s][2]=z*(r-l+1);
        return;
    }
    int wz=(l+r)/2;
    if (tree[s][0]!=-1)
    {
        tree[s*2][0]=tree[s*2][1]=tree[s][0];
        tree[s*2+1][0]=tree[s*2+1][1]=tree[s][0];
        tree[s*2][2]=(wz-l+1)*tree[s][0];
        tree[s*2+1][2]=(r-wz)*tree[s][0];
        tree[s][0]=-1;
    }
    if (y<=wz) change(s*2,l,wz,x,y,z);
        else if (x>wz) change(s*2+1,wz+1,r,x,y,z); else
        {
            change(s*2,l,wz,x,wz,z);
            change(s*2+1,wz+1,r,wz+1,y,z);
        }
    tree[s][1]=max(tree[s*2][1],tree[s*2+1][1]);
    tree[s][2]=tree[s*2][2]+tree[s*2+1][2];
}
void search(int s,int l,int r,int x,int y)
{
    if (l==x && r==y)
    {
        ans+=tree[s][2];
        return;
    }
    int wz=(l+r)/2;
    if (tree[s][0]!=-1)
    {
        tree[s*2][0]=tree[s*2][1]=tree[s][0];
        tree[s*2+1][0]=tree[s*2+1][1]=tree[s][0];
        tree[s*2][2]=(wz-l+1)*tree[s][0];
        tree[s*2+1][2]=(r-wz)*tree[s][0];
        tree[s][0]=-1;
    }
    if (y<=wz) search(s*2,l,wz,x,y);
        else if (x>wz) search(s*2+1,wz+1,r,x,y); else
        {
            search(s*2,l,wz,x,wz);
            search(s*2+1,wz+1,r,wz+1,y);
        }
    tree[s][1]=max(tree[s*2][1],tree[s*2+1][1]);
    tree[s][2]=tree[s*2][2]+tree[s*2+1][2];
}
int main()
{
    scanf("%d",&n);
    fo(i,1,n) scanf("%lld",&c[i]);
    fo(i,1,n)
    {
        if (c[i]<=n)
        {
            next[bz[c[i]]]=i;
            bz[c[i]]=i;
        }
        while (bz[p]) p++;
        _1[i]=p;
        ans+=p;
    }
    build(1,1,n);
    fo(i,1,n-1)
    {
        if (next[i]==0) next[i]=n+1;
        k=find(1,1,n,c[i]);
        if (tree[bj][1]<c[i]) k=n+1;
        if (k<=next[i]-1) change(1,1,n,k,next[i]-1,c[i]);
        search(1,1,n,i+1,n);
    }
    printf("%lld",ans);
}

——2016.8.11

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值