【洛谷】P2638 安全系统 题解
在题解区看了半天发现没有 \(O(n)\) 的做法,估计是因为n的范围实在太小了。
同时还出现了两种完全不同的题意解释,但是最终答案却完全相同,都是正确的。
本文将先阐释 \(O(n)\) 的做法,再证明为何两种解释的答案会相同。
题目链接:P2638 安全系统
(对题意困惑的童鞋请跳到第二部分)
1. 算法
思路
这里我对题目的解释是:所有的1信号相同,所有0信号相同,并且一个存储器可以存储多个相同信号。
首先最初的想法其他题解已经讲的很清楚了,我就不多浪费文字了。
两种信号互相不影响,只需要分别用插板法计算 \(a\) 与 \(b\) 个信号存储在 \(n\) 个存储器中的答案,再将二者相乘即可。
其中信号0的存储方法种数:
看到其他题解里有说用数学方法可以做到 \(O(n)\) 。确实,我第一个想到的就是用数学方法简化。
- 数学方法
首先有大名鼎鼎的组合数递推公式:
其中 \(\binom{n}{r}\) 为二项式,即 \(\operatorname{C}_n^r\) (注意上下标的位置,不要搞反)。
从而:
然后我们就利用它做一个裂项:
而单个二项式的计算是 \(O(n)\) 的。
然而,很快我发现,并不需要用数学方法来简化它
- 组合方法
我们来看我们之前的柿式子是怎么推出来的:是将 \(i\) 个信号放入 \(n\) 个存储区内的放法,然后对每个 \(i (0\le i \le a)\) 利用插板法。
由于每个0相同,我们可以再多放一个存储区,用来存储没有存储在前 \(n\) 个存储区内的信号,也就是 \(a\) 个信号放入 \((n+1)\) 个存储区,其中存储区内信号个数为非负整数。
这样就是将 \(n\) 个板插入 \((a+n+1)-1=(a+n)\) 个空,方法数为:
至此 \(O(n)\) 的算法就讲解完了。(实际上还可以取\(n\),\(a\)的最小值来计算)
代码
代码十分简洁,只需要计算组合数再把两种情况乘起来就彳亍了。
#include<cstdio>
using namespace std;
unsigned long long bino(int n, int r) {//计算二项式。
unsigned long long ans = 1;
for (int i = 1; i <= r; ++i) {
ans *= n - i + 1;
ans /= i;//边乘边除是为了防爆范围,做除法时整除证明不多阐述。
}
return ans;
}
int main() {
int n, a, b;
scanf("%d %d %d", &n, &a, &b);
printf("%llu\n", bino(a+n, n)*bino(b+n, n));
return 0;
}
2. 题意
这道题题意是有歧义的,并没有明确说明每个存储区究竟是可以存放多个信号还是只能存放一个,样例中的 \(a\) 与 \(b\) 也都是 \(1\),无法由此判断。第一次提交的时候我也没搞清题意,暴零了。
一脸迷惑的我进入题解区,看到两种不同解释,更迷惑了。
总结一下,大致有两种解释:
-
所有的1信号相同,所有0信号相同,并且一个存储器可以存储多个相同信号。
-
所有的1、0信号不同,并且一个存储器至多能存储一个0和一个1
第一种解释就是我之前所说的,这里不多解释。
第二种解释给出的答案是:
大致是说在 \(n\) 个存储区内选 \(i\) 个存储区放信号,然后再从 \(a\) 个信号中选 \(i\) 个存储。
仔细想想是有问题,因为如果每个0信号不同,应当答案再乘以 \(i\) 个0信号的排列。如果真是按照第二种解释,应当不是这个答案。也就是说,第二种解释是错误的。
然而,最有趣的就是这个答案虽然是错的,但是可以通过本题。经过一个上午的思考,我发现这是个歪打正着。
下面证明两种解释答案的结果是相同的。
- 数学方法
我们用到大名鼎鼎的范德蒙德卷积公式!
其证明可以用生成函数,也可以有组合证明,具体见这里。
于是:
这样就得到了我们之前算出的式子。
- 组合方法
我们可以从这个角度理解第一种解释:我们选 \(i\) 个存储区, 把 \(a\) 个信号放入这 \(i\) 个存储区和一个独立于 \(n\) 个存储区,存放剩下的信号的存储区。
这样存信号种数有 \(\binom{a}{i}\) 个,而选出 \(i\) 个存储区的方法有 \(\binom{n}{i}\)种。
从而第一种解释的式子变为:
这与第二种解释的式子是相同的。
完结撒花(?