【Luogu4921】情侶?給我燒了!(組合計數)
阿新 • • 發佈:2018-12-25
【Luogu4921】情侶?給我燒了!(組合計數)
題面
題解
很有意思的一道題目。
直接容斥?怎麼樣都要一個平方複雜度了。
既然是恰好\(k\)對,那麼我們直接來做:
首先列舉\(k\)對人出來\(\displaystyle {n\choose k}\),然後枚\(k\)排座位出來\(\displaystyle {n\choose k}\),這些人間的順序關係\(k!\),然後這些人可以左右交換\(2^{k}\)。
好的,現在的問題轉化為了剩下\(n-k\)對人,兩兩之間不能坐在一排,求方案數。
首先這\(n-k\)對人的順序提前算好\((n-k)!\),然後左右順序忽視掉\(2^{n-k}\)
假裝\(n\)對人完全錯開的方案數是\(f(n)\)。
類似錯排問題,然而並不是錯排問題。類似錯排問題的遞推公式的想法,每次加入最新的一組。
那麼當前這一組隨便和前面哪一排找個人互換就好了,一共有兩種交換方法。所以這一部分的貢獻是\((n-1)*2*2*2*f(n-1)\)。
還有特殊情況就是原本換的那組兩個人在一排,現在和這一排強制交換,有兩種交換方法。那麼這部分的貢獻就是\((n-1)*2*f(n-2)\)。
那麼轉移湊合一下就是\(f(n)=2(n-1)(f(n-1)+f(n-2))\)。
再把答案式寫一下:
\[Ans_k=2^n{n\choose k}^2k!(n-k)!f(n-k)\]
這樣子預處理\(f\)之後單次的複雜度為\(O(n)\)。
不過我還看到了一種很有意思的方法。
設\(f[i][j]\)表示\(i\)對情侶中恰好有\(j\)對坐在一起的方案數,\(g[i]\)表示\(i\)對情侶都不坐在一起的方案數。
那麼\(\displaystyle f[n][k]={n\choose k} A_n^k2^k*g[n-k]\)
那麼反過來\(\displaystyle g[n]=(2n)!-\sum_{i=1}^n f[n][i]\)
這樣子是\(O(n^2)\)的,感覺很有意思的方法。
程式碼是前面那種方法
#include<iostream> #include<cstdio> using namespace std; #define MAX 1010 #define MOD 998244353 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,f[MAX],jc[MAX],jv[MAX],inv[MAX],bin[MAX]; int C(int n,int m){return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;} int main() { int T=read();jc[0]=jv[0]=inv[0]=inv[1]=f[0]=bin[0]=1; for(int i=1;i<MAX;++i)f[i]=2ll*(i-1)*(f[i-1]+f[i-2])%MOD; for(int i=2;i<MAX;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD; for(int i=1;i<MAX;++i)jc[i]=1ll*jc[i-1]*i%MOD; for(int i=1;i<MAX;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD; for(int i=1;i<MAX;++i)bin[i]=2ll*bin[i-1]%MOD; while(T--) { n=read(); for(int i=0;i<=n;++i) printf("%lld\n",1ll*bin[n]*C(n,i)%MOD*C(n,i)%MOD*jc[n-i]%MOD*jc[i]%MOD*f[n-i]%MOD); } return 0; }
```