码迷,mamicode.com
首页 > 其他好文 > 详细

HDU 4455 Substrings (2012年杭州赛区现场赛C题)

时间:2015-08-30 14:23:31      阅读:153      评论:0      收藏:0      [点我收藏+]

标签:dp   找规律   

1.题目描述:点击打开链接

2.解题思路:本题利用dp解决。不过这个dp的思路的确比太容易想到。需要观察规律才能发现。我们可以从贡献值的角度考虑。以题目中给的样例来说明这种方法。

技术分享

通过观察相邻两个w值,我们会发现一个事实:每个大区间都包含了小区间的解(这里的解即原题中的sum值)。但是这还不够,观察图上标记为红色的数字,它们可以看做是上一个w值对应的区间多出来的新数字,如果我们可以算出这些红色数字的贡献值,那么我们就能得到当前w下的解。那么该如何计算呢?


继续观察后会发现,每次w增加1,区间个数就会减少1个,也就是说,w-1时候的最后一个区间的结果要被减掉,才是w-1的解对当前w的贡献。接下来看红色数字的贡献,由于当前区间长度为w,那么某个数字i和前一个i的距离差<w时,这些数字都不能算入。比如以w=5的情况为例,w=5时候,第一个红色数字4要被算入,因为它和前一个4(如果不存在前一个数就看做在0位置,数组的起始下标从1开始)距离为5,不满足距离<w,因此要被算入,而第二个红色数字4和前一个4距离为1,满足1<w的要求,因此不能被算入。也就是说,我们需要知道数字的距离分别有几个,比如距离为x的有dis[x]个,那么w-1时候的红色数字的贡献值假设为sum,那么w时候,红色数字的贡献就是sum-dis[w-1]。同时,我们用last[i]表示最后i个数字有几种不同的数字。于是得到如下的递推式:

dp(w)=dp(w-1)-last(w-1)+(sum-dis[w-1]);

上式中的sum是w-1时候红色数字对答案的贡献。当w=0时候,sum=n。这样就可以用O(1)时间得到所有的dp(w)了。当然,根据上述分析,我们需要提前预处理出来dis[i]和last[i],详细过程见代码。

本题最终的时间复杂度是O(N+Q)。

3.代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<functional>

using namespace std;

#define me(s) memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <int, int> P;


const int N = 1000010;
int a[N], dis[N], vis[N], last[N], sum;
ll dp[N];

int n;

int main()
{

	while (~scanf("%d", &n) && n)
	{
		sum = n; //sum是前一个w值中红色数字对答案的贡献值
		me(vis); me(dis);
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &a[i]);
			dis[i - vis[a[i]]]++; //统计不同距离的个数
			vis[a[i]] = i;//更新a[i]上一次出现的位置
		}
		me(vis);
		last[1] = 1;//last[i]表示最后i个数中有几个不同的数
		vis[a[n]] = 1;//标记a[n]访问过
		for (int i = 2; i <= n; i++)
			if (!vis[a[n - i + 1]])//发现新的没有出现过的数,last[i]++
			{
				vis[a[n - i + 1]]++;
				last[i] = last[i - 1] + 1;
			}
			else last[i] = last[i - 1];
			dp[1] = n;
		for (int i = 2; i <= n; i++)
		{
			sum -= dis[i - 1];//更新sum,得到当前w时候红色数字对答案的贡献值
			dp[i] = dp[i - 1] - last[i - 1] + sum; 
		}
		int q;
		scanf("%d", &q);
		for (int i = 0; i<q; i++)
		{
			int w;
			scanf("%d", &w);
			printf("%I64d\n", dp[w]);
		}
	}
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

HDU 4455 Substrings (2012年杭州赛区现场赛C题)

标签:dp   找规律   

原文地址:http://blog.csdn.net/u014800748/article/details/48104099

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!