ARC061F - Card Game for Three
可以发现当把出牌顺序看成一个序列, 每个序列对应的就是一种局面
例如"bc"我们可以当成a先打了一张b, 然后b打了一张c, 然后轮到c结束了(因为是a先开始)
转换出来之后我们就可以把问题抽象成, 有多少个序列, 其存在一个前缀, 满足这个前缀包含n个a, 不超过m个b, 不超过k个c
那么, 容易发现最短的前缀他一定是以a结尾的, 那么, 我们通过枚举前缀长度, 可以得到一个显然的$O(N^2)$做法
$$ans = \sum_{i=0}^m \sum_{j=0}^k C(n+i+j-1, i)C(n+j-1, j)3^{m+k-i-j}$$
其中i表示b的个数, j表示c的个数, 当满足这个前缀之后, 后面的出牌顺序和牌的种类已经不重要了, 所以后面有$3^{m+k-i-j}$
现在考虑优化这个式子
给这个式子变化一下形式
$=\sum_{i=0}^m \sum_{j=0}^k C(n+i+j-1, i)C(n+j-1, j)3^{m+k-i-j} \\ = \sum_{i=0}^m \sum_{j=0}^k \frac{(n+i+j-1)!}{i!(n+j-1)!} \frac{(n+j-1)!}{j!(n-1)!}3^{m+k-i-j}\\ = \sum_{i=0}^m \sum_{j=0}^k \frac{(n+i+j-1)!}{(n-1)!(i+j)!} \frac{(i+j)!}{j!i!}3^{m+k-i-j} \\ =\sum_{i=0}^m \sum_{j=0}^k C(n+i+j-1, n-1) C(i+j, j) 3^{m+k-j-i} \\ = \sum_{i=0}^{m+k} \sum_{j=max(i-m, 0)}^{min(i, k)} C(n+i-1, n-1) C(i, j) 3^{m+k-i}\\ = \sum_{i=0}^{m+k}C(n+i-1, n-1)3^{m+k-i} \sum_{j=max(i-m, 0)}^{min(i, k)} C(i, j)$
其实上式已经分离了两个变量, 我们只需要解决如何快速的求后面这个东西, 即$\sum_{j=max(i-m, 0)}^{min(i, k)} C(i, j)$
然后我们发现两个界限 $max(i-m, 0)\leq j\leq min(i, k)$
也就是说我们可以分成三段
第一段, i <= k, 此时 $\sum_{0}^{i} C(i, j)=2^i$
第二段, 当i>k, 此时$\sum_{j=0}^{k} C(i, j)$是一个组合数前k项和的形式, 这个东西可以用一个线性递推来解决, 设$f[i]$表示组合数i的前k项和, 那么有$f[i] = 2f[i-1] - C(i-1, k)$
具体解释如图:
那么$f[i] - 1= 2f[i-1] - C(i-1, k) - C(i-1, 0)$
因为$C(i,j) = C(i-1, j) + C(i-1, j-1)$
第三段, 当i>m,上面两个加多了的东西得减掉, 也就是$\sum_{j=0}^{i-m-1} C(i, j)$, 这个东西同样可以用一个线性递推来解决, 设$g[i]$表示前$i-m-1$项和, 因为$i-m-1$肯定不会超过k, 所以当i+1的时候j也在+1, 如图
那么$g[i] -1 = 2g[i-1] + C(i-1, i-m-1) - C(i-1, 0)$ 因为不论是上面还是下面, 0都只会被记一次, 而下面这个式子里, 我们需要补一个$C(i-1, i-m-1)$来组成$C(i, i-m-1)$
最后只需要把前两段加起来然后减去第三段的东西就可以了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define pb push_back
#define ln '\n'
const int N = 9e5+5;
const int mod = 1e9+7;
inline void inc(int &a, int b){
a+=b;
if(a>=mod) a-=mod;
}
inline void dec(int &a, int b){
a-=b;
if(a<0) a+=mod;
}
inline int power(int a, int b){
int res = 1;
for(; b; b >>= 1, a=1ll*a*a%mod)
if(b&1)
res=1ll*res*a%mod;
return res;
}
int fac[N], inv[N], f[N], g[N];
inline void prework(int n){
fac[0] = 1;
for(int i = 1; i <= n; i++)
fac[i] = 1ll * fac[i-1] * i % mod;
inv[n] = power(fac[n], mod-2);
for(int i = n; i; i--)
inv[i-1] = 1ll * inv[i] * i % mod;
}
inline int C(int n, int m){
return 1ll * fac[n] * inv[m] % mod * inv[n-m] % mod;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n, m, k;
cin >> n >> m >> k;
prework(n+m+k);
int ans = 0;
for(int i = 0; i <= k; i++)
inc(ans, 1ll*C(n+i-1, n-1)*power(3, m+k-i)%mod*power(2, i)%mod);
f[k] = power(2, k);
for(int i = k + 1; i <= m+k; i++){
f[i] = (2*f[i-1]%mod - C(i-1, k) + mod) % mod;
inc(ans, 1ll*C(n+i-1, n-1)*power(3, m+k-i)%mod*f[i] % mod);
}
g[m] = 0;
for(int i = m+1; i <= m+k; i++){
g[i] = (2*g[i-1]%mod + C(i-1, i-m-1)) % mod;
dec(ans, 1ll*C(n+i-1, n-1)*power(3, m+k-i)%mod*g[i] % mod);
}
cout << ans << ln;
}

浙公网安备 33010602011771号