1. 程式人生 > >[P4921] 情侶?給我燒了!

[P4921] 情侶?給我燒了!

回顧一下錯排公式

錯排問題:
設n位錯排數為D[n]。考慮元素1的位置,設定為k(有n-1中 );在考慮元素k的位置,
若為1,則轉換為n-2位的錯排;否則,視元素k為元素1(不能放在位置1),轉換為n-1位的錯排。
故 D[n]=(n-1)(D[n-1]+D[n-2]) D[1]=0 D[2]=1
也有公式D[n]=n!/e (e=1-1/1!+1/2!-1/3!+……+(-1)^n*1/n!)

設g(x)為x對情侶都在x排裡都錯開的方案數。
對於詢問的k,其答案=C(n,k)A(n,k)pow(2,k)*g(n-k)

式子從左到右依次表示:
1)在n排中選出k排
2)每對情侶中佔某一排排
3)情侶雙方的位置互調
4)剩下n-k對情侶都要錯開。

然後考慮g(x)的演算法。

這是就要聯想到錯排序列的構造方法。
考慮第一排的情況:
1)倆男(x(x-1)種),考慮他們對應的倆女
a)這倆女在同一排((x-1)
2種方案),此時轉化為g(x-2)。
b)這倆女不在同一排, 就把她們當做一對情侶,轉化為g(x-1)
2)倆女,與倆男同
3)左男右女,仔細想想是與倆男還是一樣的。
4)左女右男,仔細想想是與倆男還還是一樣的。

故轉移為g(x)=(1+1+1+1)(x(x-1)((x-1)2*g(x-2)+g(x-1)))=4x(x-1)(2(x-1)g(x-2)+g(x-1))
顯然g(1)=0, g(2)=8 //於是g(0)=1...

時間複雜圖 O(Tn)

#include <bits/stdc++.h>
using namespace std;

const int N=1e3+7;
const int P=998244353;

int fac[N]={1};
int inv[N]={0,1};
int fiv[N]={1};
int g[N]={1};
int b[N]={1};

inline int C(int x,int y) {
    return 1LL*fac[x]*fiv[y]%P*fiv[x-y]%P;
}
inline int A(int x,int y) {
    return 1LL*fac[x]*fiv[x-y]%P;
}
inline int f(int n,int k) {
    return 1LL*C(n,k)%P*A(n,k)%P*b[k]%P*g[n-k]%P;
}

int main() {
    for(int i=1; i<N; ++i) fac[i]=1LL*fac[i-1]*i%P;
    for(int i=2; i<N; ++i) inv[i]=1LL*(P-P/i)*inv[P%i]%P;
    for(int i=1; i<N; ++i) fiv[i]=1LL*inv[i]*fiv[i-1]%P;
    for(int i=2; i<N; ++i) g[i]=4LL*i*(i-1)*(2LL*(i-1)*g[i-2]+g[i-1])%P;
    for(int i=1; i<N; ++i) b[i]=b[i-1]*2LL%P;
    int T,n;
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        for(int k=0; k<=n; ++k) {
            printf("%d\n",f(n,k));
        }
    }
    return 0;
}