[省选联考 2020 A 卷] 组合数问题

[省选联考 2020 A 卷] 组合数问题

\[\newcommand\string[2]{\genfrac{\{}{\}}{0pt}{}{#1}{#2}} \newcommand\down[2]{{#1}^{\underline{#2}}} \]

壹、传送门 ¶

传送门 to Luogu.

贰、题解 ¶

利用了这个式子:

\[{n\choose k}\times \down{k}{m}={n-m\choose k-m}\times \down{n}{m} \]

首先,对于 \(f(k)\),考虑将每一项拆开计算:

\[\begin{aligned} &\sum_{k=0}^nk^ix^k{n\choose k} \\ =&\sum_{k=0}^nb_i{n\choose k}\down{k}{i}x^k \\ =&\sum_{k=0}^nb_i{n-i\choose k-i}\down{n}{i}x^k \\ =&\sum_{i=0}^mb_i\down{n}{i}\sum_{k=0}^n{n-i\choose k-i}x^k \\ =&\sum_{i=0}^mb_i\down{n}{i}\sum_{k=0}^{n-i}{n-i\choose k}x^k\times x^i \\ =&\sum_{i=0}^mb_ix^i\down{n}{i}\sum_{k=0}^{n-i}{n-i\choose k}x^k\\ =&\sum_{i=0}^mb_ix^i\down{n}{i}(x+1)^{n-i} \end{aligned} \]

对于 \(b_i\),由于一定有:

\[\begin{aligned} \sum_{i=0}^ma_ik^i&=\sum_{i=0}^ma_i\sum_{j=0}^i\string{i}{j}\down{k}{j} \\ &=\sum_{j=0}^m\down{k}{j}\sum_{i=j}^m\string{i}{j}a_i \end{aligned} \]

那么,就有

\[b_j=\sum_{i=j}^m\string{i}{j}a_i \]

对于第二类斯特林数,有递推

\[\string{n}{m}=\string{n-1}{m-1}+m\string{n-1}{m} \]

注意,我们得特殊定义

\[\string{0}{0}=1 \]

然后,我们就可以在 \(\mathcal O(m^2)\) 的复杂度内解决这个问题了。

叁、参考代码 ¶

#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;

// #define NDEBUG
#include<cassert>

namespace Elaina{
    #define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
    #define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
    #define fi first
    #define se second
    #define mp(a, b) make_pair(a, b)
    #define Endl putchar('\n')
    #define mmset(a, b) memset(a, b, sizeof a)
    typedef long long ll;
    typedef pair<int, int> pii;
    typedef pair<ll, ll> pll;
    template<class T>inline T fab(T x){ return x<0? -x: x; }
    template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
    template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
    template<class T>inline T readin(T x){
        x=0; int f=0; char c;
        while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
        for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
        return f? -x: x;
    }
    template<class T>inline void writc(T x, char s='\n'){
        static int fwri_sta[1005], fwri_ed=0;
        if(x<0) putchar('-'), x=-x;
        do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
        while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
        putchar(s);
    }
}
using namespace Elaina;

const int maxm=1000;

inline int qkpow(int a, int n, int p){
    int ret=1;
    for(; n>0; n>>=1, a=1ll*a*a%p)
        if(n&1) ret=1ll*ret*a%p;
    return ret;
}

int a[maxm+5], n, x, p, m;

int stir[maxm+5][maxm+5];

inline void initial(){
    stir[0][0]=1; // pay attention!
    rep(i, 1, maxm) rep(j, 1, i)
        stir[i][j]=(stir[i-1][j-1]+1ll*j*stir[i-1][j]%p)%p;
}

inline void input(){
    n=readin(1), x=readin(1), p=readin(1), m=readin(1);
    rep(i, 0, m) a[i]=readin(1);
}

int b[maxm+5];

inline void getb(){
    rep(j, 0, m) rep(i, j, m)
        b[j]=(b[j]+1ll*stir[i][j]*a[i]%p)%p;
}

inline int down(int x, int k){
    int ret=1;
    rep(i, x-k+1, x) ret=1ll*ret*i%p;
    return ret;
}

inline void getans(){
    int ans=0;
    rep(i, 0, m) ans=(ans+1ll*b[i]*qkpow(x, i, p)%p*down(n, i)%p*qkpow(x+1, n-i, p)%p)%p;
    writc(ans);
}

signed main(){
    input();
    initial();
    getb();
    getans();
    return 0;
}

肆、关键的地方 ¶

首先考虑证明

\[{n\choose k}\times \down{k}{m}={n-m\choose k-m}\times \down{n}{m} \]

\[\begin{aligned} &{n!\over k!(n-k)!}\times \down{k}{m} \\ =&{n!\over (n-k)!(k-m)!} \\ =&{(n-m)!\over (n-k)!(k-m)!}\times \down{n}{m} \\ =&{n-m\choose k-m}\times \down{n}{m} \\ \end{aligned} \]

\(\blacksquare.\)

但是是怎么想到这样推柿子的呢?乱搞吧。

posted @ 2021-06-04 21:18  Arextre  阅读(112)  评论(0编辑  收藏  举报