【AT3576】Popping Balls(数数题)
- 有 \(n\) 个红球和 \(m\) 个蓝球排成一行。
- 你可以任意选定两个数 \(x,y\),然后执行 \(n+m\) 次取球操作,每次在第 \(1\)/\(x\)/\(y\) 个球中取走一个。
- 求取出的球的颜色序列有多少种可能。
- \(1\le n,m\le2\times10^3\)
组合数学
选定不同的 \(x,y\) 可能取出相同的颜色序列这一点显得比较麻烦,考虑让每种颜色序列对应唯一的一种 \(x,y\)。
比较显然,红球肯定可以视作是从 \(1\) 取的;蓝球优先视作从 \(y\) 取,其次视作从 \(x\) 取,最后视作从 \(1\) 取。
我们强定第一次取蓝球的位置就是 \(y\),因为真正的 \(y\) 肯定大于等于我们选中的这个 \(y\) ,而当其大于这个 \(y\) 时肯定不会更优。则接下来 \(m\) 步可以随意选择取红球或是蓝球, \(m\) 步后 \(y\) 位置上就没球了。
同理,强定之后第一次取蓝球的位置就是 \(x\),设 \(y\) 位置取走了 \(i\) 次蓝球,那么接下来 \(m-i\) 步可以随意选择取红球或是蓝球,\(m-i\) 步后 \(x\) 位置上就没球了,此后只能全部从 \(1\) 取。
于是,我们枚举在 \(y\) 位置取走了 \(i\) 次蓝球(取走了 \(m-i\) 次红球),\(x\) 位置取走了 \(j\) 次蓝球(取走了 \(m-i-j\) 次红球),由于第一次取的肯定是蓝球,方案数分别为 \(C_{m-1}^{i-1}\) 和 \(C_{m-i-1}^{j-1}\)。
剩下的 \(n-(m-i)-(m-i-j)\) 个红球可以分别放在 \(y\) 首次取球前、\(x,y\) 取球间、\(x\) 取完球后的三部分中(剩下的蓝球只能在 \(x\) 取完球后所有红球都取走后),利用插板法计算出方案数为 \(C_{n-(m-i)-(m-i-j)+2}^2\)。
注意,上面的过程中假定了蓝球会分为从 \(y\) 取和从 \(x\) 取两部分,而没有考虑一次取走所有蓝球的情况,这一部分的方案数为 \(n+1\)。
代码:\(O(n^2)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 2000
#define X 1000000007
using namespace std;
int n,m,C[2*N+5][2*N+5];
I void Init(CI S) {RI i,j;for(C[0][0]=i=1;i<=S;++i) for(C[i][0]=j=1;j<=i;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%X;}
int main()
{
RI i,j,t;for(scanf("%d%d",&n,&m),t=n+1,Init(max(n+2,m)),i=1;i<=m;++i) for(j=1;j<=m-i;++j)//枚举在y,x位置上取走的蓝球数
(m-i)+(m-i-j)<=n&&(t=(t+1LL*C[m-1][i-1]*C[m-i-1][j-1]%X*C[n-(m-i)-(m-i-j)+2][2])%X);return printf("%d\n",t),0;//组合数计算方案数
}
待到再迷茫时回头望,所有脚印会发出光芒