题目:https://cn.vjudge.net/problem/FZU-2037
题意:给出一个序列,由1~n这n个数组成,给出一个最大值Max,初始化为零。问按照题目给出的代码求这个序列的最大值需要更新多少次Max,将这个值暂记为m。题目所求该序列的所有排列的m的总和summ,以及summ/该序列总排列个数。
举个例子:
n = 3时
序列{1, 2, 3} —— m = 3
序列{1, 3, 2} —— m = 2
序列{2, 1, 3} —— m = 2
序列{2, 3, 1} —— m = 2
序列{3, 1, 2} —— m = 1
序列{3, 2, 1} —— m = 1
summ = 11
summ / 排列数 = 11 / 6 = 1.833333
思路:
这题需要递推,从1推向n。
显然初始条件summ(1) = 1
从n-1推向n有两种情况
(1)n这个数放在原序列的最后,因为n就是最大的数,所以对于原序列每个m都要多更新一次,原序列一共有(n - 1)种排列
那么第一种情况summ1(n) = summ(n - 1) + 1 * (n - 1)!
(2)n这个数放在其他地方
n = 3,n-1 = 2时
1,2 = 2
2,1 = 1
3,1,2 = 1
1,3,2 = 2
3,2,1 = 1
2,3,1 = 2
n = 4,n-1 = 3时,如图
可以多写几组,注意观察,颜色相同的序列,它们的最后一位的数字都相同,它们每组的summ都为summ(n-1),共有n-1组
那么第二种情况summ2(n) = summ(n - 1) * (n - 1)
综上,总递推式
summ(n)
= summ(n-1) + (n-1)! + summ(n-1)*(n-1)
= n*summ(n-1) + (n-1)!
那么第二个结果问题可以根据以上递推式除上个n!可以推出
ans2(n) = ans2(n-1) + 1.0 / ans2(n-1)
别忘了处理取模
代码:
//GNU C++上%lld会WA
//需用%I64d
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 1000000 + 10;
const long long MOD = 1000000007;
long long fact[maxn];
long long val[maxn];
double ans[maxn];
void init()
{
fact[1] = 1;
for(int i = 2; i < maxn; i++)
{
fact[i] = fact[i - 1] * i % MOD;
}
val[1] = 1;
for(int i = 2; i < maxn; i++)
{
val[i] = i * val[i - 1] % MOD;
val[i] += fact[i - 1];
val[i] %= MOD;
}
ans[1] = 1;
for(int i = 2; i < maxn; i++)
{
ans[i] = ans[i - 1] + 1.0 / i;
}
}
int main()
{
init();
int cases = 1;
int T;
cin >> T;
while(T--)
{
int n;
scanf("%d", &n);
printf("Case %d: %I64d %.6f\n", cases++, val[n], ans[n]);
}
return 0;
}
2787

被折叠的 条评论
为什么被折叠?



