红书《题目与解读》第一章 数学 题解《ACM国际大学生程序设计竞赛题目与解读》
Posted 繁凡さん
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了红书《题目与解读》第一章 数学 题解《ACM国际大学生程序设计竞赛题目与解读》相关的知识,希望对你有一定的参考价值。
整理的算法模板合集: ACM模板
实际上是一个全新的精炼模板整合计划
红书《题目与解读》第一章 数学 题解《ACM国际大学生程序设计竞赛题目与解读》
目录
第一章 数学
1.1 概率
Problem A.Coupons (几何概型,概率)
UVA 10288
Problem
一共有 n n n 种不同的优惠券,每次得到一个优惠券的概率相同,问期望多少次得到所有 n n n 种优惠券,以带分数的形式输出。
Solution
方法一:
设 f [ i ] f[i] f[i] 表示已经买到 i i i 个优惠券的期望购买次数。
考虑最后一次购买,若买到的是一个新优惠券,则:
f [ i ] + = ( f [ i − 1 ] + 1 ) × n − ( i − 1 ) n f[i] += (f[i-1]+1)\\times \\cfrac{n-(i-1)}{n} f[i]+=(f[i−1]+1)×nn−(i−1)
若买到的是一个已经买过但不是第 i i i 个买的优惠券,则:
f [ i ] + = ( f [ i ] + 1 ) × i − 1 n f[i]+=(f[i]+1)\\times \\frac{i-1}{n} f[i]+=(f[i]+1)×ni−1
整理得:
f [ i ] = f [ i − 1 ] + n n − i + 1 f[i]=f[i-1]+\\frac{n}{n-i+1} f[i]=f[i−1]+n−i+1n
即:
a n s = ∑ i = 1 n n n − i + 1 = ∑ i = 1 n n i ans = \\sum_{i = 1}^{n}\\cfrac{n}{n-i+1}=\\sum_{i=1}^{n}\\cfrac{n}{i} ans=i=1∑nn−i+1n=i=1∑nin
显然最后的答案就是调和级数前缀和。
若数据较大的话可以 O ( 1 ) O(1) O(1) 计算调和级数前缀和:
调和级数 ∑ i = 1 ∞ 1 n \\displaystyle\\sum_{i = 1}^{∞}\\cfrac{1}{n} i=1∑∞n1 的极限为 ln n + C \\ln n+C lnn+C,其中 C = 0.57721566490153286060651209 C=0.57721566490153286060651209 C=0.57721566490153286060651209 是欧拉常数
方法二:
红书上的题解
当前已有 k k k 种,显然得到新优惠券的概率为 n − k n \\cfrac {n-k} n nn−k,显然是几何概型,所以期望是 n n − k \\cfrac {n}{n-k} n−kn,所以答案就是 n n + n n − 1 + ⋯ + n 1 = n × ∑ i = 1 n 1 i \\displaystyle \\cfrac n n+ \\cfrac {n}{n-1}+\\cdots+\\cfrac{n}{1}=n\\times \\sum\\limits_{i=1}^{n}\\cfrac 1 i nn+n−1n+⋯+1n=n×i=1∑ni1
Hint
数据较大,注意约分,除掉 gcd \\gcd gcd
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
//#define ll __int128;
typedef long long ll;
const int N = 107;
int n, m;
int up[N], down[N];
ll lcm(int a, int b)
{
return a / __gcd(a, b) * b;
}
int get_len(int x)
{
int len = 0;
while(x) {
x /= 10;
len ++ ;
}
return len;
}
void solve()
{
ll LCM = 1;
for(int i = 1; i <= n; ++ i) {
up[i] = n;
down[i] = i;
LCM = lcm(LCM, i);
}
ll sum = 0;
for(int i = 1; i <= n; ++ i) {
sum += n * (LCM / i);
}
ll d = __gcd(sum, LCM);
sum /= d;
LCM /= d;
if(LCM == 1) {
cout << sum << endl;
return ;
}
ll mo = sum % LCM;
ll l = sum / LCM;
for(int i = 1; i <= get_len(l) + 1; ++ i) cout << " ";
cout << mo << endl;
cout << l << " ";
for(int i = 1; i <= get_len(LCM); ++ i) cout << "-";
puts("");
for(int i = 1; i <= get_len(l) + 1; ++ i) cout << " ";
cout << LCM << endl;
}
signed main()
{
while(scanf("%lld", &n) != EOF) {
solve();
}
return 0;
}
Problem B.Generator (KMP,期望,高斯消元)
ZOJ 2619
Problem
给定一个字符串 S S S 和字符集大小 n n n 。要求另生成一个字符串,它一开始为空,每次平均且独立地随机生成一个字符集中的字符添加到其末尾,生成出字串 S S S 时停下,求所生成字符串的长度的期望。
Solution
显然生成的字符串越来越长,每次由 n n n 种字符选择,那么就有 i n i^n in 种方案数,杂乱无章的无从下手。所以从对答案的贡献角度出发,发现对于答案而言,有用的只有最后生成的字符串 T T T 的后缀与模式串 S S S 的匹配长度。因此很多杂乱的字符串实际上对于答案而言是同一种状态,即一共只有 0 ∼ L 0\\sim L 0∼L 种状态,表示两字符串匹配的长度。
这样就有了清晰的状态,考虑状态如何转移即可。
书中倒推由于都是未知的需要使用高斯消元解方程组,比较麻烦,精度还不能得到保障。我们这里利用一个小技巧,直接正推。利用 KMP , O ( n ) O(n) O(n) 求出失配数组 nex i , j \\text{nex}_{i,j} nexi,j(当然要在失配的时候用)
反过来设 f[i]
为从状态
0
0
0 到状态
i
i
i 期望次数,答案显然就是 f[len]
则可以把原转移方程直接改写为:
f [ i ] = f [ i + 1 ] n + 1 n ∑ j = 0 n − 1 f [ nex [ i + ′ A ′ ] ] − 1 f[i] = \\frac{f[i+1]}{n}+\\frac{1}{n}\\sum_{j=0}^{n-1}{f[\\text{nex}[i + 'A\\ ']]} - 1 f[i]=nf[i+1]+n1以上是关于红书《题目与解读》第一章 数学 题解《ACM国际大学生程序设计竞赛题目与解读》的主要内容,如果未能解决你的问题,请参考以下文章
《题目与解读》红书 训练笔记目录《ACM国际大学生程序设计竞赛题目与解读》