2019 沈阳网络赛 E. Gugugu's upgrade schemes(贝尔数 | Touchard同余)

2019 沈阳网络赛 E. Gugugu's upgrade schemes(贝尔数 | Touchard同余)_第1张图片


贝尔数:https://www.cnblogs.com/yuyixingkong/p/4480349.html
利用Touchard同余性质, B n + p = ( B n + B n + 1 ) m o d    p ( p ∈ p r i m e ) B_{n + p} = (B_n + B_{n + 1}) \mod p (p \in prime) Bn+p=(Bn+Bn+1)modp(pprime)
当 n 较小时可以用贝尔数的计算式 n 2 n ^2 n2 计算得出,当 n 较大时可以记忆化搜索,预处理n < 1000的贝尔数。


代码:

#include
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
int t,mod,n;
ll dp[maxn],fact[maxn],ifact[maxn];
ll C(int x,int y) {
	if(y > x || y < 0) return 0;
	return fact[x] * ifact[y] % mod * ifact[x - y] % mod;
}
ll fpow(ll a,ll b) {
	ll r = 1;
	while(b) {
		if(b & 1) r = r * a % mod;
		b >>= 1;
		a = a * a % mod;
	}
	return r;
}
ll dfs(ll x) {
	if(x < 0) return 0;
	if(dp[x]) return dp[x];
	return dp[x] = (dfs(x - mod) + dfs(x - mod + 1)) % mod;
}
int main() {
	scanf("%d",&t);
	while (t--) {
		memset(dp,0,sizeof dp);
		memset(fact,0,sizeof fact);
		memset(ifact,0,sizeof ifact);
		scanf("%d%d",&n,&mod);
		fact[0] = 1;
		for (int i = 1; i <= mod - 1; i++)
			fact[i] = fact[i - 1] * i % mod;
		ifact[mod - 1] = fpow(fact[mod - 1],mod - 2);
		for (int i = mod - 2; i >= 0; i--) 
			ifact[i] = ifact[i + 1] * (i + 1) % mod;
		dp[0] = dp[1] = 1;
		for(int i = 2; i <= 1000; i++)
			for(int j = 0; j < i; j++)
				dp[i] = (dp[i] + C(i - 1,j) * dp[j] % mod) % mod;
		printf("%lld\n",dfs(n));
	}
	return 0;
}

你可能感兴趣的:(贝尔数,组合数学,记忆化搜索)