Mex is a function on a set of integers, which is universally used for impartial game theorem. For a non-negative integer set S, mex(S) is defined as the least non-negative integer which is not appeared in S. Now our problem is about mex function on a sequence.
Consider a sequence of non-negative integers {ai}, we define mex(L,R) as the least non-negative integer which is not appeared in the continuous subsequence from aL to aR, inclusive. Now we want to calculate the sum of mex(L,R) for all 1 <= L <= R <= n.
给n个数,求所有区间内没有出现的最小非负整数和。
输入描述
The input contains at most 20 test cases.For each test case, the first line contains one integer n, denoting the length of sequence.The next line contains n non-integers separated by space, denoting the sequence.(1 <= n <= 200000, 0 <= ai <= 10^9)The input ends with n = 0.
输出描述
For each test case, output one line containing a integer denoting the answer.
样例
Sample1
输入
3 0 1 3 5 1 0 2 0 1 0
输出
5 24
样例解释
For the first test case:mex(1,1)=1, mex(1,2)=2, mex(1,3)=2, mex(2,2)=0, mex(2,3)=0,mex(3,3)=0.1 + 2 + 2 + 0 +0 +0 = 5.
解题思路
解题前首先要明白mex的一个性质:一个长为n的非负集合的mex值的范围只可能为0~n。所以对于数组中大于等于n的值可以忽略。
对于一个非负集合,求mex,最简单的办法就是先在集合中寻找0是否存在,再在集合中寻找1是否存在,直到寻找到一个i在集合中不存在,则mex为i。
已知n个数(a1,a2,...,an)和这n个数所有以a1开始的区间的mex值的和sum。如果我们在这n个数的最左边再插入一个数a0,求新序列的所有以a0开始的区间的mex值的和sum。由mex的性质可知,以a1开始的区间的mex值:mex(a1)、mex(a1,a2)、mex(a1,a2,a3)、...、mex(a1,a2,...,an)是递增的。假设ai是索引i最小的等于a0的值,则mex(a0,a1,..,ai,...)的值并不会因为a0的加入而改变,依旧等于mex(a1,..,ai,...)。这时,我们只需计算mex(a1)、mex(a1,a2)、...、mex(a1,a2,...,ai-1)等值因a0的加入而改变的值。
计算mex(a1)、mex(a1,a2)、...、mex(a1,a2,...,ai-1)等值因a0的加入而改变的值。mex(a1,a2,...,aj)(j<i)是否改变取决于a1,a2,...,aj是否完全包含0到a0-1。
在a0,a1,a2,...,an中,令pos[k]第一次出现k的位置,令dp[k]为完全包含0到k的最小区间的右索引(若a0,a1,..,aj是包含0到k的区间,且j最小,则dp[k]=j)。有如下递推关系:
dp[j]=max(dp[j-1],pos[j])
sum改变的值也就可以确定了。
#include<stdio.h>
#include<string>
#include<algorithm>
#define N 200005
using namespace std;
int main()
{
int n, tmp, least;
int a[N];
int pos[N];//pos[i]记录i出现的最小位置
int dp[N];//dp[i]为包含0到i的最小区间的右索引
long long sum, ans;
while (scanf("%d", &n) && n)
{
for (int i = 1; i <= n; i++)
scanf("%d", a + i);
sum = 0;
ans = 0;
fill(pos, pos + n + 1, n + 1);
fill(dp, dp + n + 1, n + 1);
for (int i = n; i > 0; i--)
{
if (a[i] < n)
{
least = pos[a[i]];
pos[a[i]] = i;
for (int j = a[i]; j < n; j++)
{
if (j)
dp[j] = max(dp[j - 1], pos[j]);
else
dp[j] = i;
if (dp[j] < least)
sum += least - dp[j];
else
break;
}
}
ans += sum;
}
printf("%lld\n", ans);
}
}