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题)
原文地址:http://blog.csdn.net/u014800748/article/details/48104099