CF1548C The Three Little Pigs(组合数学+dp)

题目传送门

题意

这道题实际上就是让求 ∑ i = 1 n C 3 ∗ i x , x \displaystyle\sum_{i=1}^{n}C_{3*i}^x,x i=1∑n​C3∗ix​,x是我们每次询问的数值, n n n是小猪进场的轮数。

思路

我们可以定义一个 d p dp dp方程, d p [ x ] [ m ] dp[x][m] dp[x][m]代表 ∑ i = 1 n − 1 C 3 ∗ i + m x \displaystyle\sum_{i=1}^{n-1}C_{3*i+m}^x i=1∑n−1​C3∗i+mx​
因此,我们可以得到 d p [ x ] [ 0 ] + d p [ x ] [ 1 ] + d p [ x ] [ 2 ] = ∑ i = 1 3 ∗ n − 1 C i x dp[x][0]+dp[x][1]+dp[x][2]=\displaystyle\sum_{i=1}^{3*n-1}C_{i}^x dp[x][0]+dp[x][1]+dp[x][2]=i=1∑3∗n−1​Cix​
又根据杨辉三角的性质我们可以得到

C i x + C i x − 1 = C i + 1 x C_i^x+C_i^{x-1}=C_{i+1}^x Cix​+Cix−1​=Ci+1x​

因此

d p [ x ] [ 1 ] = d p [ x ] [ 0 ] + d p [ x − 1 ] [ 0 ] dp[x][1]=dp[x][0]+dp[x-1][0] dp[x][1]=dp[x][0]+dp[x−1][0]
d p [ x ] [ 2 ] = d p [ x ] [ 1 ] + d p [ x − 1 ] [ 1 ] dp[x][2]=dp[x][1]+dp[x-1][1] dp[x][2]=dp[x][1]+dp[x−1][1]

又根据公式
∑ i = k n C i k = C n + 1 k + 1 \displaystyle\sum_{i=k}^{n}C_{i}^k=C_{n+1}^{k+1} i=k∑n​Cik​=Cn+1k+1​
因此 d p [ x ] [ 0 ] + d p [ x ] [ 1 ] + d p [ x ] [ 2 ] = C 3 ∗ n x + 1 dp[x][0]+dp[x][1]+dp[x][2]=C_{3*n}^{x+1} dp[x][0]+dp[x][1]+dp[x][2]=C3∗nx+1​

现在递推式每一个状态下的 d p [ x ] [ 1 ] , d p [ x ] [ 2 ] dp[x][1],dp[x][2] dp[x][1],dp[x][2]都能求出来了,现在就来求 d p [ x ] [ 0 ] dp[x][0] dp[x][0]
根据上述方程。

d p [ x ] [ 1 ] = d p [ x ] [ 0 ] + d p [ x − 1 ] [ 0 ] dp[x][1]=dp[x][0]+dp[x-1][0] dp[x][1]=dp[x][0]+dp[x−1][0]
d p [ x ] [ 2 ] = d p [ x ] [ 1 ] + d p [ x − 1 ] [ 1 ] dp[x][2]=dp[x][1]+dp[x-1][1] dp[x][2]=dp[x][1]+dp[x−1][1]
d p [ x ] [ 0 ] + d p [ x ] [ 1 ] + d p [ x ] [ 2 ] = C 3 ∗ n x + 1 dp[x][0]+dp[x][1]+dp[x][2]=C_{3*n}^{x+1} dp[x][0]+dp[x][1]+dp[x][2]=C3∗nx+1​

代入原方程求得:

d p [ x ] [ 0 ] + d p [ x ] [ 0 ] + d p [ x − 1 ] [ 0 ] + d p [ x ] [ 1 ] + d p [ x − 1 ] [ 1 ] = C 3 ∗ n x + 1 dp[x][0]+dp[x][0]+dp[x-1][0]+dp[x][1]+dp[x-1][1]=C_{3*n}^{x+1} dp[x][0]+dp[x][0]+dp[x−1][0]+dp[x][1]+dp[x−1][1]=C3∗nx+1​
d p [ x ] [ 0 ] + d p [ x ] [ 0 ] + d p [ x − 1 ] [ 0 ] + d p [ x ] [ 0 ] + d p [ x − 1 ] [ 0 ] + d p [ x − 1 ] [ 1 ] = C 3 ∗ n x + 1 dp[x][0]+dp[x][0]+dp[x-1][0]+dp[x][0]+dp[x-1][0]+dp[x-1][1]=C_{3*n}^{x+1} dp[x][0]+dp[x][0]+dp[x−1][0]+dp[x][0]+dp[x−1][0]+dp[x−1][1]=C3∗nx+1​

整理一下:

3 ∗ d p [ x ] [ 0 ] + 2 ∗ d p [ x − 1 ] [ 0 ] + d p [ x − 1 ] [ 1 ] = C 3 ∗ n x + 1 3*dp[x][0]+2*dp[x-1][0]+dp[x-1][1]=C_{3*n}^{x+1} 3∗dp[x][0]+2∗dp[x−1][0]+dp[x−1][1]=C3∗nx+1​

因此

d p [ x ] [ 0 ] = ( C 3 ∗ n x + 1 − 2 ∗ d p [ x − 1 ] [ 0 ] − d p [ x − 1 ] [ 1 ] ) / 3 dp[x][0]=(C_{3*n}^{x+1}-2*dp[x-1][0]-dp[x-1][1])/3 dp[x][0]=(C3∗nx+1​−2∗dp[x−1][0]−dp[x−1][1])/3

同时,官方题解的逆元求组合数的板子比我的强了不知道多少倍。

#include<vector>
#define vi vector<int>
#define vl vector<long long>
typedef long long ll;
const ll MOD=1e9+7;
struct Combo {
    vl facs;
    vl invfacs;
    int N;
 
    Combo(int N) {
        this->N=N;
        facs.assign(N+1,0);
        invfacs.assign(N+1,0);
        facs[0] = 1;
        for (int i = 1; i <= N; i++) {
            facs[i] = (facs[i-1]*i)%MOD;
        }
        invfacs[N] = power(facs[N],MOD-2);
        for (int i = N-1; i >= 0; i--) {
            invfacs[i] = (invfacs[i+1]*(i+1))%MOD;
        }
    }
 
    ll choose(int n, int k) {
        if (n<0||k<0||n<k) return 0LL;
        ll denInv = (invfacs[k]*invfacs[n-k])%MOD;
        ll ans = (facs[n]*denInv)%MOD;
        return ans;
    }
 
    ll power(ll x, ll y) {
        ll ans = 1;
        x %= MOD;
        while (y > 0) {
            if (y%2==1) ans = (ans*x)%MOD;
            y /= 2;
            x = (x*x)%MOD;
        }
        return ans;
    }
};
#include<iostream>
#include<vector>
#define vi vector<int>
#define vl vector<long long>
using namespace std;
typedef long long ll;
const ll N=1e9+7;
const int M=3e6+5;
const ll MOD=1e9+7;
struct Combo {
    vl facs;
    vl invfacs;
    int N;
 
    Combo(int N) {
        this->N=N;
        facs.assign(N+1,0);
        invfacs.assign(N+1,0);
        facs[0] = 1;
        for (int i = 1; i <= N; i++) {
            facs[i] = (facs[i-1]*i)%MOD;
        }
        invfacs[N] = power(facs[N],MOD-2);
        for (int i = N-1; i >= 0; i--) {
            invfacs[i] = (invfacs[i+1]*(i+1))%MOD;
        }
    }
 
    ll choose(int n, int k) {
        if (n<0||k<0||n<k) return 0LL;
        ll denInv = (invfacs[k]*invfacs[n-k])%MOD;
        ll ans = (facs[n]*denInv)%MOD;
        return ans;
    }
 
    ll power(ll x, ll y) {
        ll ans = 1;
        x %= MOD;
        while (y > 0) {
            if (y%2==1) ans = (ans*x)%MOD;
            y /= 2;
            x = (x*x)%MOD;
        }
        return ans;
    }
};
ll dp[M][4];
int main(){
	ll n;
    cin>>n;
	Combo c(3*n);
    ll inv=c.power(3,MOD-2);
    dp[0][0]=dp[0][1]=dp[0][2]=n;
    for(int i=1;i<n*3;i++){
    	dp[i][0]=(c.choose(3*n,i+1)-2*dp[i-1][0]-dp[i-1][1]+3*MOD)*inv%MOD;
    	dp[i][1]=(dp[i-1][0]+dp[i][0])%MOD;
    	dp[i][2]=(dp[i-1][1]+dp[i][1])%MOD;
	}
    int m;
    cin>>m;
    while(m--){
    	int q;
    	scanf("%d",&q);
    	printf("%lld\n",(ll)(dp[q][0]+c.choose(3*n,q))%MOD);
	}
}
上一篇:厦大C语言上机 1365 小明的自娱自乐


下一篇:vue 使用mock.js