[HDU3439]Sequence

題目

傳送門 to HDU

思路

錯排是衆所周知的。這是一個計數,求一一對應關係 y=f(x)y=f(x) 的數量。該函數滿足 x[1,n]Z,f(x)x\forall x\in[1,n]\cap\Z,f(x)\ne x 。記錄爲 d(n)d(n)

我們用容斥去計算。枚舉至少有多少個 xx 滿足 f(x)f(x) ,就可以寫出

d(n)=i=0n(1)i(ni)(ni)!=i=0n(1)in!i!(ni)!(ni)!=i=0n(1)in!i!=i=0n(1)ij=i+1nj\begin{aligned} d(n)&=\sum_{i=0}^{n}(-1)^i{n\choose i}(n-i)!\\ &=\sum_{i=0}^{n}(-1)^i\cdot\frac{n!}{i!\cdot(n-i)!}\cdot(n-i)!\\ &=\sum_{i=0}^{n}\frac{(-1)^i\cdot n!}{i!}\\ &=\sum_{i=0}^{n}(-1)^i\prod_{j=i+1}^{n}j \end{aligned}

然後用倒數第二行的公式,可以看出遞推式 d(n)=nd(n1)+(1)nd(n)=n\cdot d(n-1)+(-1)^n

nn 是用來補充 n!n! 中相較於 d(n1)d(n-1)(n1)!(n-1)! 的缺失,(1)n(-1)^n 是加入的最後一項,就是 i=ni=n 的時候。

但是,這個遞推是 O(n)\mathcal O(n) 的,我沒法優化它。想個辦法,不如計算一下 d(2p+1)modpd(2p+1)\bmod p 的值。用公式的最後一行。注意到 j=i+1nj\prod_{j=i+1}^{n}j 這一項在 i+12pi+1\le 2pi2p1i\le 2p-1 的時候,都包含了 2p2p 這一因數,故在取模意義下爲零。我們只剩下兩項,i{2p,2p+1}i\in\{2p,2p+1\} 的貢獻,單獨算。

d(2p+1)1(2p+1)+1×10(modp)d(2p+1)\equiv -1\cdot(2p+1)+1\times 1\equiv 0\pmod{p}

畢竟 2p+11(modp)2p+1\equiv 1\pmod{p} 。然鵝,我們知道 d(1)=0=d(2p+1)d(1)=0=d(2p+1),這時候我們可以用遞推公式計算 d(2p+2)d(2p+2) 了。

d(2p+2)=(2p+2)d(2p+1)+(1)2p+22d(1)+(1)2(modp)=d(2)\begin{aligned} d(2p+2)&=(2p+2)\cdot d(2p+1)+(-1)^{2p+2}\\ &\equiv 2\cdot d(1)+(-1)^2\pmod{p}\\ &=d(2) \end{aligned}

注意到了嗎?兩個相乘的數字 (2p+k+1)(2p+k+1)d(2p+k)d(2p+k) 在模 pp 意義下同時出現了循環。

以此類推,d(2p+3)d(3),d(2p+4)d(4),,d(2p+k)d(k)d(2p+3)\equiv d(3),d(2p+4)\equiv d(4),\dots,d(2p+k)\equiv d(k) 都是成立的。

最後的結論就是,dd 出現了循環。我們有 d(2p+k)=d(k)(k>0)d(2p+k)=d(k)\quad(k>0)

注意 d(2p)d(0)d(2p)\ne d(0) 。所以不要直接取模 2p2p

現在我們只需要用 O(p)\mathcal O(p) 的時間計算了。然後這道題要求 (nk)d(nk)modm{n\choose k}d(n-k)\bmod m ,就很簡單了唄。組合數用 LucasLucas 求解。

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章