红书《题目与解读》第一章 数学 题解《ACM国际大学生程序设计竞赛题目与解读》

整理的算法模板合集: 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[i1]+1)×nn(i1)

若买到的是一个已经买过但不是第 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)×ni1

整理得:

f [ i ] = f [ i − 1 ] + n n − i + 1 f[i]=f[i-1]+\frac{n}{n-i+1} f[i]=f[i1]+ni+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=1nni+1n=i=1nin

显然最后的答案就是调和级数前缀和。

若数据较大的话可以 O ( 1 ) O(1) O(1) 计算调和级数前缀和:

调和级数 ∑ i = 1 ∞ 1 n \displaystyle\sum_{i = 1}^{∞}\cfrac{1}{n} i=1n1 的极限为 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 nnk,显然是几何概型,所以期望是 n n − k \cfrac {n}{n-k} nkn,所以答案就是 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+n1n++1n=n×i=1ni1

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 0L 种状态,表示两字符串匹配的长度。

这样就有了清晰的状态,考虑状态如何转移即可。
在这里插入图片描述

书中倒推由于都是未知的需要使用高斯消元解方程组,比较麻烦,精度还不能得到保障。我们这里利用一个小技巧,直接正推。利用 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]+n1j=0n1f[nex[i+A ]]1
就是 f [ i ] f[i] f[i] 由下一步匹配成功的 f [ i + 1 ] f[i+1] f[i+1] 与未匹配成功的 ∑ j = 0 n − 1 f [ nex [ i + ′ A   ′ ] ] \displaystyle \sum_{j=0}^{n-1}{f[\text{nex}[i + 'A\ ']]} j=0n1f[nex[i+A ]] 减去一次期望操作转移而来。

化简成正推的形式即:
f [ i + 1 ] = ( f [ i ] + 1 ) × n − ∑ j = 0 n − 1 f [ nex [ i + ′ A   ′ ] ] f[i+1] = (f[i] + 1)\times n - \sum_{j=0}^{n-1}{f[\text{nex}[i + 'A\ ']]} f[i+1]=(f[i]+1)×nj=0n1f[nex[i+A ]]

初始化 f[0] = 0,然后 O ( n ) O(n) O(n) 正序递推即可。

Code

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
const int N = 50;

int n, m, k, t, ans, kcase, cases;
int a[N];
int nex[N];
char s[N];
ll f[N];
int len;

void get_nex(char* s)
{
    
	for (int i = 2, j = 0; i <= len; ++ i) {
   
		while(j != 0 && s[j + 1] != s[i])
			j = nex[j];
		if(s[j + 1] == s[i])
			++ j;
		nex[i] = j;
	}
}

void solve()
{
    
	scanf("%d%s", &n, s + 1);
	len = strlen(s + 1);
	get_nex(s);
	f[0] = 0;
	for (int i =0; i <= len - 1; ++ i) {
   
		f[i + 1] = (f[i] + 1) * n;
		for (int j = 0; j < n; ++ j) {
   
			if(s[i + 1] == 'A' + j) 
				continue; 
			int pos = i;
			while(pos && s[pos + 1] != j + 'A')
				pos = nex[pos];
			if(s[pos + 1] == j + 'A')
				++ pos;
			f[i + 1] -= f[pos];
		}
	}
	printf("%lld\n", f[len]);
}

int main()
{
   
	scanf("%d", &t); 
	while(t -- ) {
   
		printf("Case %d:\n", ++ kcase);
		solve();
		if(t)
		    puts("");
	}
	return 0;
}

Problem C.Dinner with Schwarzenegger!!! (概率)

UVA10217

Problem

有若干人排队买电影票,如果某个人的生日与排在他前面的某个人的生日相同,那么他讲中奖。中奖的机会只有一个,给所有中奖者中排在最前面的那一位。排在第一位的人如果与买票者的生日相同,那么他将中奖。如果一年有 n n n 天,求排在什么位置的中奖概率最大,和理论上的最佳实数位置。

Solution

设第 i i i 个人的中奖概率是 f[i],显然有:

f [ 1 ] = 1 n f[1] = \cfrac 1 n f[1]=n1

f [ 2 ] = n − 1 n × 1 n f[2] = \cfrac{n-1}{n} \times \cfrac 1 n f[2]=nn1×n1

. . . ... ...

f [ i ] = n − 1 n × n − 1 n × n − 2 n × . . . × n − i + 2 n × i − 1 n f[i] = \cfrac{n-1}n \times \cfrac{n-1} n \times \cfrac{n-2} n \times ...\times \cfrac {n-i+2} n \times \cfrac{i-1} n f[i]=nn1×nn1×nn2×...×nni+2×ni1

f [ i + 1 ] = n − 1 n × n − 1 n × n − 2 n × . . . × n − i + 1 n × i n f[i+1] = \cfrac {n-1} n \times \cfrac {n-1} n \times \frac {n-2} n \times ...\times \cfrac{n-i+1} n \times \cfrac i n f[i+1]=nn1×nn1×nn2×...×nni+1×ni

f [ i ] f [ i + 1 ] = ( i − 1 ) × n ( n − i + 1 ) × i \cfrac {f[i]}{f[i+1]} = \cfrac {(i-1)\times n}{(n-i+1)\times i} f[i+1]f[i]=(ni+1)×i(i1)×n

显然概率越来越小, f [ i ] f [ i + 1 ] ≥ 1 \cfrac {f[i]}{f[i+1]} \ge 1 f[i+1]f[i]1 解得:

1 − 4 × n + 1 2 ≤ i ≤ 1 + 4 × n + 1 2 \cfrac{1-\sqrt{4\times n+1} } {2} \le i \le \cfrac{1+\sqrt{4\times n+1}} {2} 214×n+1 i21+4×n+1

最佳整数位置为 ⌈ 1 + 4 × n + 1 2 ⌉ \left \lceil\cfrac {1+\sqrt{4\times n+1}} 2\right\rceil 21+4×n+1 ,最佳实数位置为 − 1 + 4 × n + 1 2 \cfrac {-1+\sqrt{4\times n+1} }2 21+4×n+1

Code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7, maxm = maxn << 1 | 7;

int n, m, s, t;
int a[maxn];

int main()
{
   
	while(scanf("%d", &n) != EOF) {
   
		double ans = (-1.0 + sqrt(1.0 + 4.0 * n)) / 2.0;
		int ans2 = ans + 1;
		printf("%.2lf %d\n", ans, ans2);
	}
	return 0;
}

1.2 代数

1.2.1 Polya

Problem A.Arif in Dhaka (Polya,等价类计数)

UVA 10294

Problem

给你一串珠子(连接成了一个环),共有 n n n 个珠子组成,你有 t t t 种颜色,现在你来给这个珠子染色,问染成项链有多少种方法?染成手镯有多少种方法?在项链里,经过顺时针旋转后相同的算一个,在手镯里,经过顺时针旋转或者沿着对称轴兑换后一样的算一个。

Solution

Code

1.2.2 矩阵

Problem A.Tower

HDU 2971

Problem

a 1 = 1 a_1=1 a1=1,给定 a 2 a_2 a2,设 a n = 2 a 2 × a n − 1 − a n − 2 a_n=2a_2\times a_{n-1}-a_{n-2} an=2a2×an1an2,求 s n = a 1 2 + a 2 2 + ⋯ + a n 2 s_n=a_1^2+a_2^2+\cdots+a_n^2 sn=a

  • 23
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
ACM/ICPC(ACM International Collegiate Programming Contest, 国际大学生程序设计竞赛)是由国际计算机界历史悠久、颇具权威性的组织ACM(Association for Computing Machinery,国际计算机协会)主办的,世界上公认的规模最大、水平最高的国际大学生程序设计竞赛,其目的旨在使大学生运用计算机来充分展示自己分析问题和解决问题的能力。该项竞赛从1970年举办至今已历29届,一直受到国际各知名大学的重视,并受到全世界各著名计算机公司的高度关注,在过去十几年中,APPLE、AT&T、MICROSOFT和IBM等世界著名信息企业分别担任了竞赛的赞助商。可以说,ACM国际大学生程序设计竞赛已成为世界各国大学生最具影响力的国际级计算机类的赛事,是广大爱好计算机编程的大学生展示才华的舞台,是著名大学计算机教育成果的直接体现,是信息企业与世界顶尖计算机人才对话的最好机会。   该项竞赛分区域预赛和国际决赛两个阶段进行,各预赛区第一名自动获得参加世界决赛的资格,世界决赛安排在每年的3~4月举行,而区域预赛安排在上一年的9~12月在各大洲举行。   ACM/ICPC的区域预赛是规模很大、范围很广的赛事。仅在2003年参加区域预赛的队伍就有来自75个国家(地区),1411所大学的3150支代表队,他们分别在127个赛场中进行比赛,以争夺全球总决赛的73个名额,其激烈程度可想而知。 2005年第30届ACM/ICPC亚洲赛区预赛共设了北京、成都、汉城、东京等11个赛站,来自亚洲各国知名高校的各个代表队进行了激烈的角逐。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

繁凡さん

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值