【题解】P4859 [MtOI2018] 情侣?给我烧了! 题解
[MtOI2018] 情侣?给我烧了!\({}^\texttt{P4859}\)题解
定义:\(f_i\) 表示有多少种不同的就坐方案满足至少有 \(i\) 对情侣是和睦的;\(g_i\) 表示有多少种不同的就坐方案满足恰好有 \(i\) 对情侣是和睦的。
\(f_i=\displaystyle{{n\choose i}{n\choose i}}i!2^i(2n-2i)!\),\(f_i =\displaystyle{\sum{j\choose i}g_j}\)。
所以
\[\begin{align*}
g_i&=\displaystyle{\sum{j\choose i}(-1)^{i-j}f_j}\\
&=\displaystyle{\sum\frac{j!}{i!(j-i)!}(-1)^{j-i}f_j}\\
&=\displaystyle{\frac1{i!}\sum\frac{(-1)^{j-i}}{(j-i)!}j!f_j}&(i\le j \le n)\\
\end{align*}
\]
定义多项式 \(A_i = i!f_i\),\(B_i=\displaystyle{\frac{(-1)^{n-i}}{(n-i)!}}\),\(C=A\times B\),使用 \(\texttt{NTT}\)。
则 \(g_i=\displaystyle{\frac1{i!}}C_{i+n}\)。
Code
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
namespace gtx{
// Fast IO
void read(int &x){
x = 0;int h = 1;char tmp;
do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
x*=h;
}
void read(char &x){do{x=getchar();}while(x==' '||x=='\n'||x=='\r');}
void write(char x){putchar(x);}
void write(int x){
if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
do st[++tot]=x%10,x/=10; while(x);
while(tot){putchar(st[tot--]+'0');}
}
void write(int x,char y){write(x);write(y);}
#ifndef int
void read(long long &x){
x = 0;int h = 1;char tmp;
do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
x*=h;
}
void write(long long x){
if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
do st[++tot]=x%10,x/=10; while(x);
while(tot){putchar(st[tot--]+'0');}
}
void write(long long x,char y){write(x);write(y);}
#endif
const int MAXN = 1000*7+10;
const int MOD = 998244353;
const int g = 3;
const int gi = 332748118;
int n,m;
int qpow(int x,int y){
if(y<0) return qpow(qpow(x,-y),MOD-2);
long long ans = 1;
long long now = x;
while(y){
if(y&1) ans*=now,ans%=MOD;
now*=now;now%=MOD;
y>>=1;
}
return ans;
}
int inv(int x){
return qpow(x,MOD-2);
}
int rev[MAXN],len;
inline void NTT(int *A,int multi){
for(int i = 0;i<len;i++){
if(i>rev[i]) swap(A[i],A[rev[i]]);
}
for(int mid = 1;mid<len;mid<<=1){
int tGn = 1;
int ttimes = qpow(qpow(g,multi),(MOD-1)/(2*mid));
for(int mergelen = 0;mergelen<len;mergelen+=(mid<<1)){
int Gn = tGn;
int times = ttimes;
for(int k = mergelen;k<mergelen+mid;k++){
A[k] = (A[k]+(A[k+mid]*Gn)%MOD)%MOD;
A[k+mid] = ((A[k]-2*A[k+mid]%MOD*(Gn)%MOD)%MOD+MOD)%MOD;
Gn *= times;Gn %= MOD;
}
}
}
}
int A[MAXN],B[MAXN];
int frac[MAXN],invfrac[MAXN];
int C(int n,int m){
return frac[n]*invfrac[m]%MOD*invfrac[n-m]%MOD;
}
int f(int i){
return C(n,i)*C(n,i)%MOD*frac[(2*n-2*i)]%MOD*qpow(2,i)%MOD*frac[i]%MOD;
}
signed main(){
read(n);
frac[0] = invfrac[0] = 1;
for(int i = 1;i<=2*n;i++){
frac[i] = frac[i-1]*i%MOD;
invfrac[i] = inv(frac[i]);
}
for(int i = 0;i<=n;i++){
A[i] = frac[i]*f(i)%MOD;
B[i] = ((n+i)&1?-1:1)*invfrac[n-i]%MOD;
}
len = 1;
while(len<=2*n+2) len<<=1;
for(int i = n+1;i<len;i++) A[i] = 0,B[i] = 0;
for(int i = 1;i<len;i++){
rev[i] = (rev[i>>1]>>1)+(i&1)*(len>>1);
}
NTT(A,1);
NTT(B,1);
for(int i = 0;i<len;i++) A[i] *= B[i],A[i]%=MOD;
NTT(A,-1);
for(int i = 0;i<=n;i++){
write((invfrac[i]*A[i+n]%MOD*qpow(len,MOD-2)%MOD+MOD)%MOD,endl);
}
return 0;
}
}
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
// ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int T = 1;
gtx::read(T);
while(T--) gtx::main();
return 0;
}

浙公网安备 33010602011771号