HDU - 4747 Mex【dp】(详解)

1 篇文章 0 订阅

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);
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值