【数学】[MtOI2018]情侣?给我烧了!

不会生成函数 /yun

弱化版直接二项式反演乱搞做差卷积就好了。

考虑 \(f[i]\) 为至少 \(i\) 对情侣坐一起的方案数,显然 \(f[i]=\binom{n}{i}\binom{n}{i}i!2^i*(2n-2i)!\),即钦定 \(i\) 对情侣,\(i\) 排座位,情侣座位选择可以全排列,每对情侣可以选择是否交换座位,剩下的随便坐。

\(g[i]\) 为恰好 \(i\) 对情侣坐一起。

\[f[i]=\sum_{j=i}^n\binom{j}{i}g[j] \]

\[g[j]=\sum_{j=i}^n(-1)^{j-i}\binom{j}{i}f[j] \]

套路化一下发现可以 NTT 优化。

强化版考虑正难则反,能不能搞出 \(i\) 对情侣错排情况数?能的话就好做了。只要钦定 \(k\) 对情侣坐一起,剩下的错排开就好了。

设 \(g[i]\) 为只有 \(i\) 对情侣且每对情侣都错排开的情况数。\(g[0]=1,g[1]=0\)。

\[g[i]=2i(2i-2)(g[i-1]+2(i-1)g[i-2]) \]

因为是递推,所以可以不考虑各排的排列,因为每次新加入的人的不同已经等价于了。

或者说,你可以认为每次进来的人都去第一排,那么每次进来的人可能不一样,所以各排间的全排列不需要。

随便选 2 个不是情侣的人,然后要不要选他们的情侣,那我们当前问题的规模就减小 1。假如选他们的情侣,那么规模减小 2,再选他们的情侣所在的排。

事实上这种组合意义这么 nb 的我在考场上也应该想不到,倒是弱化版挺一眼的。

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
int rd() {
    int f=1,sum=0; char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return sum*f;
}
#define N 2005
#define M (int)(4e5+5)
const int mod=998244353,G=3;

int fpow(int x,int y) {
	int res=1;
	while(y) {
		if(y&1) res=res*x%mod;
		y>>=1; x=x*x%mod;
	}
	return res;
}
const int invG=fpow(G,mod-2);
void NTT(int *f,int n,bool fl=1) {
	static int tr[M];
	for(int i=0;i<n;i++) tr[i]=(tr[i>>1]>>1)|((i&1)?(n>>1):0);
	for(int i=0;i<n;i++) if(i<tr[i]) swap(f[i],f[tr[i]]);
	for(int p=2;p<=n;p<<=1) {
		int len=(p>>1),w=fpow(fl?G:invG,(mod-1)/p);
		for(int l=0;l<n;l+=p) {
			int buf=1;
			for(int i=l;i<l+len;i++) {
				int qwq=f[i+len]*buf%mod;
				f[i+len]=(f[i]-qwq)%mod;
				f[i]=(f[i]+qwq)%mod;
				buf=buf*w%mod;
			}
		}
	}
	if(fl) return ;
	int qwq=fpow(n,mod-2);
	for(int i=0;i<n;i++) f[i]=f[i]*qwq%mod; 
} 
int f[M],g[M],jie[N],djie[N],n;
int C(int n,int m) {
	if(m>n||n<0||m<0) return 0;
	return jie[n]*djie[m]%mod*djie[n-m]%mod;
}

void solve() { 
	memset(f,0,sizeof(f)); memset(g,0,sizeof(g));
	n=rd(); f[0]=jie[2*n];
	for(int i=1;i<=n;i++) f[i]=C(n,i)*C(n,i)%mod*jie[i]%mod*fpow(2,i)%mod*jie[2*n-2*i]%mod;
	// 太屑了 不放 n^3 ,你玩nmd,是个紫就要 NTT 做差卷积??
	// 老子今天都不知道写几遍 NTT 了
	for(int i=0;i<=n;i++) f[i]=jie[i]*f[i]%mod;
	for(int i=0;i<=n;i++) g[i]=((i&1)?-1:1)*djie[i]%mod;
	reverse(f,f+1+n);
	int m; for(m=1;m<n+n+2;m<<=1);
	NTT(f,m,1); NTT(g,m,1);
	for(int i=0;i<m;i++) f[i]=f[i]*g[i]%mod;
	NTT(f,m,0);
	reverse(f,f+1+n);
	for(int i=0;i<=n;i++) {
		int qwq=f[i]*djie[i]%mod;
		qwq=(qwq%mod+mod)%mod;
		printf("%lld\n",qwq);
	}
}

signed main() {
	jie[0]=1;
	for(int i=1;i<=N-5;i++) jie[i]=jie[i-1]*i%mod;
	djie[N-5]=fpow(jie[N-5],mod-2);
	for(int i=N-5;i;i--) djie[i-1]=djie[i]*i%mod;
	int T=rd();
	while(T--) solve();
	return 0;
} 
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
int rd() {
    int f=1,sum=0; char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return sum*f;
}
#define N (int)(5e6+5)
const int mod=998244353;
int g[N],jie[N],djie[N];

int fpow(int x,int y) {
	int res=1; x%=mod;
	while(y) {
		if(y&1) res=res*x%mod;
		y>>=1; x=x*x%mod; 
	}
	return res;
}

int C(int n,int m) {
	return jie[n]*djie[m]%mod*djie[n-m]%mod;
}

void solve() {
	int n,k,ans=0;
	cin>>n>>k;
	ans=C(n,k)*C(n,k)%mod*jie[k]%mod*fpow(2,k)%mod*g[n-k]%mod;
	ans=(ans%mod+mod)%mod;
	cout<<ans<<'\n';
}

signed main() {
	cin.tie(0);
	ios::sync_with_stdio(false);
	jie[0]=1; for(int i=1;i<=N-5;i++) jie[i]=jie[i-1]*i%mod;
	djie[N-5]=fpow(jie[N-5],mod-2); for(int i=N-5;i;i--) djie[i-1]=djie[i]*i%mod;
	g[0]=1; g[1]=0; // 按定义 
	for(int i=2;i<=N-5;i++) g[i]=2*i%mod*(2*i%mod-2)%mod*((g[i-1]+g[i-2]*(i-1)%mod*2%mod)%mod)%mod;
	int T; cin>>T; while(T--) solve();
	return 0;
}
上一篇:vue中router与route区别


下一篇:C++动态分配(new和malloc的用法及区别)