初见安~这里是传送门:洛谷P6620 [省选联考 2020 A 卷] 组合数问题
题解
看到题目中的很容易想到用第二类斯特林数来把k换出来降低复杂度。但是显然k也是1e9的范围所以这就meaningless了。
但是f函数中还有一个。并且m很小,只有1000。
由第二类斯特林数的式子:
可以二项式反演得到:
于是把这个反演式子带进原式子:
我们先把后面的sum代换一下,然后把f带入原式子:
到这里其实还不能算。因为这个题的模数不一定是质数,所以没法预处理阶乘逆元。那就继续推:
然后因为式子里的里,
,所以j的枚举上界其实是m。n的j次下降幂可以暴力算,斯特林数可以递推预处理,这样的话最后总的复杂度就是
。
上代码——
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxm 1003
using namespace std;
typedef long long ll;
int read() {
int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
int n, x, mod, m, a[maxm], S[maxm][maxm], g[maxm];
int pw(int a, int b) {int res = 1; while(b) {if(b & 1) res = 1ll * res * a % mod; a = 1ll * a * a % mod, b >>= 1;} return res;}
signed main() {
n = read(), x = read(), mod = read(), m = read();
for(int i = 0; i <= m; i++) a[i] = read();
S[0][0] = 1;
for(int i = 1; i <= m; i++) for(int j = 1; j <= m; j++) //斯特林数递推式
S[i][j] = (1ll * j * S[i - 1][j] % mod + S[i - 1][j - 1]) % mod;
//为了方便枚举,g做了一下移位
for(int j = 0; j <= m; j++) for(int i = 0; i <= m - j; i++)
g[m - j] = (1ll * g[m - j] + 1ll * a[i + j] * S[i + j][j] % mod) % mod;
int ans = 0;
for(int i = 0; i <= m; i++) {
int tmp = 1;
for(int j = n - i + 1; j <= n; j++) tmp = 1ll * tmp * j % mod;//求下降幂
ans = (1ll * ans + 1ll * g[m - i] * tmp % mod * pw(x + 1, n - i) % mod * pw(x, i) % mod) % mod;
}
printf("%d\n", ans);
return 0;
}
公式可能有打错了的……迎评:)
——End——