博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

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;
}

  

 

posted @ 2022-07-02 08:26  gllonkxc  阅读(27)  评论(1编辑  收藏  举报