Atcoder CODE FESTIVAL 2017 qual B E - Popping Balls 组合计数
题目链接
题意
\(A+B\)个球排成一行,左边\(A\)个为红球,右边\(B\)个为蓝球。
最开始可以选择两个数\(s,t\),每次操作可以取左起第\(1\)或\(s\)或\(t\)个球。问有多少种不同的取球序列。
Sample
Sample Input 1
3 3
Sample Output 1
20
Explanation
There are 20 ways to give 3 red balls and 3 blue balls. It turns out that all of them are possible.
Here is an example of the operation (r stands for red, b stands for blue):
You choose s=3,t=4.
Initially, the row looks like rrrbbb
.
You remove 3rd ball (r) and give it to Snuke. Now the row looks like rrbbb
.
You remove 4th ball (b) and give it to Snuke. Now the row looks like rrbb
.
You remove 1st ball (r) and give it to Snuke. Now the row looks like rbb
.
You remove 3rd ball (b) and give it to Snuke. Now the row looks like rb
.
You remove 1st ball (r) and give it to Snuke. Now the row looks like b
.
You remove 1st ball (b) and give it to Snuke. Now the row is empty.
This way, Snuke receives balls in the order rbrbrb
.
思路
官方题解
将剩下的球的序列转化成二维平面上的点,则取球过程为起点为\((A,B)\),终点为\((0,0)\)的路径。
显然,可以一直向左走,因为向左走就对应着取该序列的第一个元素;
但是向下走是需要满足一定的条件的,那就是红球的个数小于\(s\)或\(t\)中的某一个。
在阴影区域内可以随意向左走或向下走。
因此,可以枚举\((p,q)\),从\((A,B)\)到\((p,q)\)的路径条数可以通过预处理组合数然后\(O(1)\)算出,而从\((p,q)\)到\((0,0)\)的路径条数可以预处理出。
以\(q-1=4\)为例,
分界线为\(x=0\)时,有\(\binom{4}{0}\)种,
分界线为\(x=1\)时,有\(\binom{4}{0}+\binom{4}{1}\)种,
分界线为\(x=2\)时,有\(\binom{4}{0}+\binom{4}{1}+\binom{4}{2}\)种,
分界线为\(x=3\)时,有\(\binom{4}{0}+\binom{4}{1}+\binom{4}{2}+\binom{4}{3}\)种,
分界线为\(x=4\)时,有\(\binom{4}{0}+\binom{4}{1}+\binom{4}{2}+\binom{4}{3}+\binom{4}{4}\)种,
分界线为\(x=5\)时,有\(\binom{4}{0}+\binom{4}{1}+\binom{4}{2}+\binom{4}{3}+\binom{4}{4}\)种,
……
分界线为\(x=n(n\geq 4)\)时,有\(\binom{4}{0}+\binom{4}{1}+\binom{4}{2}+\binom{4}{3}+\binom{4}{4}\)种.
对于\((p,q)\),将分界线为\(x=0,1,2,...,p\)的情况累和,即得路径条数。
Code
参考:
#include <bits/stdc++.h>
#define maxn 4000
#define maxm maxn+10
using namespace std;
typedef long long LL;
LL temp[maxm][maxm], C[maxm][maxm];
const LL mod = 1e9+7;
LL add(LL a, LL b) { return (a+b) % mod; }
LL mul(LL a, LL b) { return (a*b) % mod; }
void init() {
for (int i = 0; i <= maxn; ++i) C[i][0] = 1;
for (int i = 1; i <= maxn; ++i) {
for (int j = 1; j <= i; ++j) C[i][j] = add(C[i-1][j], C[i-1][j-1]);
}
for (int i = 0; i <= maxn; ++i) {
temp[i][0] = C[i][0];
for (int j = 1; j <= maxn; ++j) {
temp[i][j] = add(temp[i][j-1], C[i][j]);
}
}
for (int i = 0; i <= maxn; ++i) {
for (int j = 1; j <= maxn; ++j) temp[i][j] = add(temp[i][j], temp[i][j-1]);
}
}
int main() {
init();
int a, b;
LL ans = 0;
scanf("%d%d", &a, &b);
for (int p = 0; p <= a; ++p) {
for (int q = 0; q <= b-1; ++q) {
if (p+q > a) continue;
if (q == 0) ans = add(ans, 1);
else ans = add(ans, mul(C[b-1][q], temp[q-1][p]));
}
}
printf("%lld\n", ans);
return 0;
}