题目
题目描述
定义一个长度为kk的数组a1,a2,⋯,ak
的妙妙趣值是:
min|ai − aj| (1 ≤ i < j ≤ k )
那么请问对于一个长度为n的序列
,所有长度为k的子序列
的妙妙趣值和为多少。
某个序列的子序列
是从最初序列通过去除某些元素但不破坏余下元素的相对位置(在前或在后)而形成的新序列。
答案为100000007
取模。
输入
第一行两个数n, k 表示序列的长度,子序列的长度。(2≤k≤n≤10002≤k≤n≤1000)
接下来一行,n个整数a1,a2,⋯,an,(0≤ai≤10^5)。
输出
一行一个数,表示妙妙趣值和。
输入样例
4 3
1 7 3 5
输出样例
8
样例解释
长度为3的子序列有 [1,7,3], [1,3,5], [7,3,5], [1,7,5],每一个妙妙趣值都是2。
题目分析与解答
第一步、
在阅读完题目描述之后,应该对子序列的定义有了了解,而本题的第一步也就是从子序列开始的。
虽然子序列是中的元素的相对位置是不可以与原序列不同的,但是本题要求的是子序列的妙妙趣值。而我们进而可以发现,妙妙趣值只与序列中差值绝对值最小的两个数有关系,与这两个数在序列中的位置无关,考虑如下序列:
2,1,4,9
1, 9, 4, 2
这两个序列中所包含的数字相同只有排列的顺序不同,但是各自的妙妙趣值都是|2 - 1| = 1
。
从而我们得出,先对输入序列进行排序后
再求其妙妙趣和值,对结果不会产生影响,因为就算我们没有遵守题目中的要求,使得子序列
的数据相对位置发生了变化,每个子序列
的妙妙趣值也不会发生改变,从而和值
也不会发生改变。
第二步、
我们现在对于一个输入序列,经过排序后,得到了一个非下降的序列。那么下面我们来思考一个问题:
一个非下降序列中,长度为k,妙妙趣和值大于等于x的序列有几个?
为什么要思考这个问题呢,因为对于这种题目,我们很显然不能
通过暴力求解的方法来拿到AC,我们需要另辟蹊径才可以。而在我们得到上面提出的问题的答案之后,解决这道题的方法也就呼之欲出了。
我们假设a[i][j]
为:
[1, i]区间内,长度为j且妙妙趣和值大于等于x的序列个数
那么我们此时就可以使用动态规划的方法来计算每个a[i][j],公式如下:
dp[i][j] = dp[i - 1][j] + dp[l1][j - 1]
其中l1
为为a[l1] + x ≤ a[i]
的最大值。
上面的的计算公式中,dp[i - 1][j]
对应的是不选取a[i]
的情况,而dp[l1][j - 1]
则是对应着选取a[i]
的情况。
第三步、
经过上面两步操作,我们已经得到了dp数据的各项数值,下面再去计算妙妙趣和值的时候就会方便很多。下面介绍两种思路
(一)
我们注意到,dp(x)是x ≤ min| a[i][j] |
的子序列个数,其中dp(x)
指代的是对应x
所计算出来的dp[n][k]
。那么显然,我们需要求的x = min| a[i][j] |
的子序列个数就是dp(x) - dp(x + 1)
。
所以我们需要做的就是枚举x
可能的值,然后在答案输出变量res
上加上x * ( dp(x) - dp(x + 1) )
。
(二)
在有了(一)的分析基础后我们再来思考一个更简单的写法,对于(一)的做法,我们可以不用每次计算
x * ( dp(x) - dp(x + 1) )
,我们可以直接枚举x
然后在res
上加上dp(x)
即可。
为什么这样做是对的呢,我们来将思路平移一下。给出:
(a1 + a2 + a3 +a4) + (a2 + a3 +a4) + (a3 + a4) + a4
明白什么意思了吗?上述表达式最终的结果是 a1 + 2a2 + 3a3 + 4a4
,如果让ai = dp(i)
的话,我们就得到
res = i * dp(i)
。而这正是我们需要的结果。
第四步、
大体上我们已经将结果计算出来了,不过我们还有一个问题并没有得到解决,那就是x到底需要枚举多少到哪一个数才可以。最简单的想法当然是把a[1]到a[n]全部枚举出来,不过这样做效率实在是太低了。我们换一个思路,我们只需要把可能的情况都枚举出来就行了。队与一个非下降序列,最大值a[n]
和最小值a[1]
的插值记为D
,那么我们只需要枚举到D / ( k - 1 )
就行了。为什么呢?因为如果存在一个子序列的妙妙趣值大于这个数那么这个子序列的最大值减去最小值D~
就会就会大于D
,显然这是不成立的。这样我们就可以显著的降低枚举的成本。
五、注意
(一)不要忘记对1e8 + 7取模
(二)在处理l1的时候,每次移动玩l1,需要对dp[j][2]
进行更改:
dp[j][2] = (dp[j - 1][2] + l1 - 1) % E;
六、题解代码
代码如下:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#define MAX 1005
const long long E = 1e8 + 7;
using namespace std;
int n, k;
int input[MAX];
long long dp[MAX][MAX];
long long res;
int com(const void *a, const void *b)
{
return *(int*)a > *(int*)b ? 1 : -1;
}
int main(void)
{
scanf("%d %d",&n, &k);
for(int i = 1; i <= n; i++)
scanf("%d",&input[i]);
qsort(input, n + 1, sizeof(int), com);//先进行排序
int end = (input[n] - input[1]) / (k - 1);
for(int i = 1; i <= end; i++)
{
int l1 = 1;
for(int j = 1; j <= n; j++)
{
while(input[l1] + i <= input[j] && l1 < j)//处理l1
{
l1++;
}
dp[j][2] = (dp[j - 1][2] + l1 - 1) % E;
for(int l = 3; l <= k; l++)
{
dp[j][l] = dp[j - 1][l] % E + dp[l1 - 1][l - 1] % E;
dp[j][l] %= E;
}
}
res += dp[n][k] % E;
res %= E;
}
printf("%lld",res);
return 0;
}