DP? - 卢卡斯 + 预处理

题目
发现最短路径是\(C_{n}^k + C_{n - 1}^{k - 1} + C_{n - 2}^{k - 2} + \dots + C_{n - k} ^ 0 + n - k\)
根据帕斯卡公式,\(C_{n}^k = C_{n - 1}^k + C_{n - 1}^{k - 1}\),将上面的式子变化一下\(C_{n - k}^0 = C_{n -k + 1}^0\)
就可以得到答案 \(C_{n + 1}^k + n - k\)
如果直接求卢卡斯,卢卡斯没预处理时的时间复杂度是\(O(plog_p^m)\)会超时,那么需要预处理
p范围是1 - 10000,那么线性筛除1 - 10000的素数有1000多个,\(n^2\)预处理一下每个素数下的阶乘和逆元
最后卢卡斯\(log_p^m\)求即可

#include <iostream>
#include <cstdio>
using namespace std;
#define ll long long
const int N = 1e4 + 5;
ll fac[N][N], inv[N][N];
int pri[N], tot, prith[N];
bool vis[N];
void pri_table(int N){
	for(int i = 2; i <= N; i++){
		if(!vis[i]){
			pri[++tot] = i;
			prith[i] = tot;
		}
		for(int j = 1; j <= tot; j++){
			if(i * pri[j] > N)break;
			vis[i * pri[j]] = 1;
			if(i % pri[j] == 0)break;
		}
	}
}
ll pow(ll a, ll b, ll p){
	ll ans = 1; a %= p;
	while(b){
		if(b & 1)ans = ans * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return ans;
}
void init(){
	for(int i = 1; i <= tot; i++){
		fac[i][0] = inv[i][0] = 1; 
		for(int j = 1; j <= pri[i]; j++){
			fac[i][j] = (fac[i][j - 1] * j) % pri[i];
			inv[i][j] = pow(fac[i][j], pri[i] - 2, pri[i]);
		}
	}
}
ll C(ll n, ll m, ll p){
	if(m > n)return 0;
	if(m == n)return 1;
	int t = prith[p];
	return fac[t][n] * inv[t][m] % p * inv[t][n - m] % p;
}
ll lucas(ll n, ll m, ll p){
	if(m == 0)return 1;
	return C(n % p, m % p, p) * lucas(n / p, m / p, p) % p;
}
int main(){
	int n, k, p;
	pri_table(10000);
	init();
	int t = 0;
	while(~scanf("%d%d%d", &n, &k, &p)){
		if(k >= n / 2)k = n - k;
		printf("Case #%d: %lld\n", ++t, (lucas(n + 1, k, p) + n - k) % p);
	}
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/Emcikem/p/12897224.html