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

不会生成函数 /yun

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

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

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

f[i]=j=in(ji)g[j]

g[j]=j=in(1)ji(ji)f[j]

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

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

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

g[i]=2i(2i2)(g[i1]+2(i1)g[i2])

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

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

随便选 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; }

__EOF__

本文作者F x o r G
本文链接https://www.cnblogs.com/xugangfan/p/15872266.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   FxorG  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示