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=1nC3ix,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=1n1C3i+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=13n1Cix
又根据杨辉三角的性质我们可以得到

C i x + C i x − 1 = C i + 1 x C_i^x+C_i^{x-1}=C_{i+1}^x Cix+Cix1=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[x1][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[x1][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=knCik=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]=C3nx+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[x1][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[x1][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]=C3nx+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[x1][0]+dp[x][1]+dp[x1][1]=C3nx+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[x1][0]+dp[x][0]+dp[x1][0]+dp[x1][1]=C3nx+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} 3dp[x][0]+2dp[x1][0]+dp[x1][1]=C3nx+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]=(C3nx+12dp[x1][0]dp[x1][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]);if(dp[i][0]<=0)dp[i][0]+=((-dp[i][0])/MOD+1)*MOD; 
		dp[i][0]=dp[i][0]*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);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值