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
", ++t, (lucas(n + 1, k, p) + n - k) % p);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Emcikem/p/12897224.html