HDU 4747 Mex 递推DP/线段树

Mex HDU - 4747

定义 mex(S)\mathrm{mex}(S) 为集合 SS 中未出现的最小非负整数。

给一个非负整数序列 {ai}i=1n\{a_i\}_{i=1}^n,定义 mex(L,R)\mathrm{mex}(L,R)aLaRa_L\sim a_R 之间未出现的最小非负整数,计算:

1LRnmex(L,R) \sum_{1\le L\le R\le n}\mathrm{mex}(L,R)

这题看得我脑壳疼…有两种方法,第一种是线段树,另一种是递推DP。

第二种方法可以看 对hdu4747 Mex 的详解

我尝试再细化一下…

首先令

f[i]=1Limex(L,i) f[i]=\sum_{1\le L\le i}\mathrm{mex}(L,i)

则有:

f[i+1]=1Li+1mex(L,i+1) \begin{aligned} f[i+1]&=\sum_{1\le L\le i+1}\mathrm{mex}(L,i+1) \end{aligned}

对于 kik\le i,有:

mex(k,i)mex(k,i+1) \mathrm{mex}(k,i)\le\mathrm{mex}(k,i+1)

因此有 f[i]f[i+1]f[i]\le f[i+1],假设我们现在已经求出来了 f[i]f[i],现在的目标是用 f[i]f[i] 推出来 f[i+1]f[i+1],最后的结果就是

res=i=1nf[i] res=\sum_{i=1}^nf[i]

(为了和代码对应,统一往前移一下)用 f[i1]f[i-1] 来推 f[i]f[i] 实际上就是想办法用 mex(k,i1)\mathrm{mex}(k,i-1) 来推 mex(k,i)\mathrm{mex}(k,i),也就是看加上 a[i]a[i] 这个元素,对集合 mex\mathrm{mex} 值的影响,首先找到元素 a[i]a[i] 之前出现时所在的下标:last=pos[a[i]]last=pos[a[i]],此时能够保证集合 {a[k]k[last+1,i]}\{a[k]|k\in[last+1,i]\}a[i]a[i] 是唯一出现的,并且就出现在下标 ii 处,而对于 jlastj\le last 对应的集合 {a[k]k[j,i]}\{a[k]|k\in[j,i]\}a[i]a[i] 不唯一出现,此时可以看到 mex(j,i1)=mex(j,i)\mathrm{mex}(j,i-1)=\mathrm{mex}(j,i) ,因为 a[i]a[i] 的作用被前一个他自己给冲掉了。所以考虑用 mex(k,i1)\mathrm{mex}(k,i-1) 来推 mex(k,i)\mathrm{mex}(k,i) 时只需考虑 klast+1k\ge last+1 的部分,之前的部分与上一次计算的相同。

所以现在考虑 klast+1k\ge last+1 时,mex(k,i)\mathrm{mex}(k,i)mex(k,i1)\mathrm{mex}(k,i-1) 多多少(很容易证明前者 \ge 后者)。

定义集合 A={0,1,,d}\mathcal{A}=\{0,1,\cdots,d\},定义 full[d]full[d] 如下:

full[d]=max{xA{a[k]k[x,i]}} full[d]=\max\{x|\mathcal{A}\subset\{a[k]|k\in[x,i]\}\}

也就是说此时集合 {a[k]k[full[d],i]}\{a[k]|k\in[full[d],i]\} 包含了集合 A\mathcal{A},并且此时 full[d]full[d] 取得是最靠右的值,考虑 a[i]d<na[i]\le d< n,首先考虑 d=a[i]d=a[i],假如 full[d]>lastfull[d]>last ,说明区间 [full[d],i][full[d],i] 之间已经包含 [0,d][0,d](也就是 [0,a[i]][0,a[i]])所有元素,又由于 a[i]a[i] 唯一(只在下标 ii 处出现过),因此区间 [full[d],i1][full[d],i-1] 之间包含 [0,d1][0,d-1] 之间所有元素,此时有:

mex(full[d],i)=d+1mex(full[d],i1)=dmex(full[d],i)=mex(full[d],i1)+1 \begin{aligned} \mathrm{mex}(full[d],i)&=d+1\\ \mathrm{mex}(full[d],i-1)&=d\\ \mathrm{mex}(full[d],i)&=\mathrm{mex}(full[d],i-1)+1\\ \end{aligned}

也就是说 mex(full[d],i)\mathrm{mex}(full[d],i) 是在 mex(full[d],i1)\mathrm{mex}(full[d],i-1) 的基础上累加了 11。再看 [k,i],k(last,full[d])[k,i],k\in(last,full[d]),的情形,假如区间 [k,full[d])[k,full[d]) 之间对应的元素仍在 [0,d][0,d] 范围内,则效果相同: mex(k,i)=mex(k,i1)+1\mathrm{mex}(k,i)=\mathrm{mex}(k,i-1)+1,而假如区间 [k,full[d]][k,full[d]] 之间的元素有些不在 [0,d][0,d] 范围内,可知这些元素必定 >d>d,因此 mex(k,i)mex(k,i1)+1\mathrm{mex}(k,i)\ge\mathrm{mex}(k,i-1)+1,也可以先累加上一个 11,至于剩下的累加是在 d>a[i]d>a[i] 时考虑进去的。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 200010
using namespace std;
typedef long long ll;
int n,a[MAXN],pos[MAXN],full[MAXN],last;
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    while(scanf("%d",&n)&&n){
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        memset(pos,0,sizeof(pos));
        memset(full,0,sizeof(full));
        ll tt=0,res=0;
        for(int i=1;i<=n;i++){
            if(a[i]<n){
                last=pos[a[i]];
                pos[a[i]]=i;
                for(int d=a[i];d<n;d++){
                    if(d)full[d]=min(full[d-1],pos[d]);
                    else full[d]=i;
                    if(full[d]>last)tt+=full[d]-last;
                    else break;
                }
            }
            res+=tt;
        }
        printf("%lld\n",res);
    }
    return 0;
}

在这里插入图片描述

posted @ 2020-03-29 11:18  winechord  阅读(189)  评论(0编辑  收藏  举报