題目
思路
錯排是衆所周知的。這是一個計數,求一一對應關係 的數量。該函數滿足 。記錄爲 。
我們用容斥去計算。枚舉至少有多少個 滿足 ,就可以寫出
然後用倒數第二行的公式,可以看出遞推式
是用來補充 中相較於 即 的缺失, 是加入的最後一項,就是 的時候。
但是,這個遞推是 的,我沒法優化它。想個辦法,不如計算一下 的值。用公式的最後一行。注意到 這一項在 即 的時候,都包含了 這一因數,故在取模意義下爲零。我們只剩下兩項, 的貢獻,單獨算。
畢竟 。然鵝,我們知道 ,這時候我們可以用遞推公式計算 了。
注意到了嗎?兩個相乘的數字 和 在模 意義下同時出現了循環。
以此類推, 都是成立的。
最後的結論就是, 出現了循環。我們有
注意 。所以不要直接取模 。
現在我們只需要用 的時間計算了。然後這道題要求 ,就很簡單了唄。組合數用 求解。
代碼
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int_;
inline int readint(){
int x; scanf("%d",&x); return x;
}
inline int qkpow(int base,int q,int Mod){
int ans = 1; base %= Mod;
for(; q; q>>=1,base=1ll*base*base%Mod)
if(q&1) ans = 1ll*ans*base%Mod;
return ans;
}
int exgcd(int a,int b,int_ &x,int_ &y){
if(b == 0){
x = 1, y = 0; return a;
}
int d = exgcd(b,a%b,y,x);
y -= (a/b)*x; return d;
}
int getInv(int x,int p){
int_ res, useless;
if(exgcd(x%p,p,res,useless) != 1)
return -1; // impossible
return (res%p+p)%p;
}
int CRT(int n,int m[],int c[]){
int M = 1, ans = 0;
for(int i=1; i<=n; ++i) M *= m[i];
for(int i=1; i<=n; ++i){
int tmp = M/m[i];
tmp *= getInv(tmp,m[i]);
ans = (ans+1ll*tmp*c[i])%M;
}
return ans;
}
int function(int n,int p,int pk){
int res = 1; if(n < 2) return 1;
if(n >= pk){
for(int i=2; i<=pk; ++i)
if(i%p != 0)
res = 1ll*res*i%pk;
res = qkpow(res,n/pk,pk);
}
for(int i=2; i<=n%pk; ++i)
if(i%p != 0)
res = 1ll*res*i%pk;
return 1ll*res*function(n/p,p,pk)%pk;
}
int calc(int n,int p){
if(n < p) return 0; return n/p+calc(n/p,p);
}
int exLucas(int n,int m,int p,int pk){
int pt = calc(n,p)-calc(m,p)-calc(n-m,p);
int jb = function(n,p,pk); // zxy orz
jb = 1ll*jb*getInv(function(m,p,pk),pk)%pk;
jb = 1ll*jb*getInv(function(n-m,p,pk),pk)%pk;
return 1ll*jb*qkpow(p,pt,pk)%pk;
}
int mods[50], yu[50];
int Lucas(int n,int m,int p){
int cnt = 0;
for(int i=2,j; 1ll*i*i<=p; ++i){
if(p%i != 0) continue;
for(j=1; p%i==0; j*=i) p /= i;
mods[++ cnt] = j;
yu[cnt] = exLucas(n,m,i,j);
}
if(p != 1){
mods[++ cnt] = p;
yu[cnt] = exLucas(n,m,p,p);
}
return CRT(cnt,mods,yu);
}
signed main(){
for(int T=readint(),kase=0; T; --T){
int n = readint(), m = readint();
int p = readint(), cp = 1;
if(n < m or p == 1){
printf("Case %d: 0\n",++kase);
continue;
}
int t = (n-m-1)%(p<<1)+1;
for(int i=1; i<=t; ++i)
cp = (1ll*cp*i+1-(i<<1&2))%p;
if(cp < 0) cp += p;
printf("Case %d: %lld\n",++kase,1ll*Lucas(n,m,p)*cp%p);
}
return 0;
}