ICPC2024南京站I题题解
看!一道计数题!我们有救了!
设
考虑进行阿贝尔变换,得到:
然后考虑
先把序列里头所有小于
统计有多少种填法,记为
于是这就须要求
那么就是有
(a)
表示第 行存在至少一个白色,其中(b)
表示第 列存在至少一个白色,其中
那么有
这种限制很不好弄,但是每个限制的补集却十分的简洁:某一行或某一列全为黑色
那么考虑容斥,就是枚举强制违反那些限制,忽略其他限制,而不难发现这个东西可以使用一个组合数来计算,并且你只关心违反了几个行限制,几个列限制,具体是谁并不关心,因此这是一个二项式反演,然后推式子
显然后面那一坨仅与
则
显然这是一个卷积的形式,因此使用多项式乘法即可
把
代码(3kb的多项式能一遍过,我是神人)
#include <bits/stdc++.h> #define mpr make_pair #define int long long #define pb push_back #define pii pair<int,int> #define st first #define nd second using namespace std; inline int read(){ int x=0,f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar())f^=ch=='-'; for(;isdigit(ch);ch=getchar())x=x*10+(ch^48); return f?x:-x; } mt19937 rnd(time(0)); const int mo=998244353; inline int qpow(int x,int t){ int ret=1; for(;t;t>>=1,x=x*x%mo)if(t&1)ret=ret*x%mo; return ret; } inline void red(int &x){x>=mo?x-=mo:0;} inline void chmin(int &x,int y){x=min(x,y);} inline void chmax(int &x,int y){x=max(x,y);} namespace poly{ const int N=2e6+5,G=3,iG=332748118; int cir[N],w[N],r[N],sav[N]; void fft(int *f,int len,int t){ for(int i=0;i<len;++i){ cir[i]=(cir[i>>1]>>1)|((i&1)?len>>1:0); if(i>cir[i])swap(f[i],f[cir[i]]); } for(int l=2;l<=len;l<<=1){ int w=qpow(t?G:iG,(mo-1)/l); for(int i=0;i<len;i+=l){ int fw=1,u,v; for(int j=i;j<i+l/2;++j,fw=fw*w%mo){ u=f[j],v=f[j+l/2]; red(f[j]=u+fw*v%mo); red(f[j+l/2]=u+mo-fw*v%mo); } } } int r=qpow(len,mo-2); for(int i=0;(!t)&&i<len;++i)f[i]=f[i]*r%mo; } void polymul(int *f,int *g,int len){ for(int i=0;i<len;++i)f[i]=f[i]*g[i]%mo; } void mul(int *f,int *g,int len){ fft(f,len,1),fft(g,len,1); polymul(f,g,len); fft(f,len,0); } } const int N=4e6+5; int n,m,fac[N],ifac[N],a[N],f[N],g[N],len,v[N]; inline int binom(int x,int y){ if(y>x||y<0||x<0)return 0; return fac[x]*ifac[y]%mo*ifac[x-y]%mo; } int sgn(int x){return x&1?-1:1;} void solve(){ n=read(),m=read(); for(int i=0;i<=n*m*3;++i)f[i]=g[i]=v[i]=0; for(int i=1;i<=n*m;++i)a[i]=read(); for(int x=0;x<=n;++x)for(int y=0;y<=m;++y) red(v[x*y]+=sgn(x+y)*binom(n,x)%mo*binom(m,y)%mo); for(int i=0;i<=n*m;++i){ v[i]=v[i]*sgn(n+m)%mo*fac[i]%mo; g[n*m-i]=ifac[i]; } len=1; while(len<=n*m*2)len<<=1; poly::mul(g,v,len); for(int i=0;i<=n*m;++i) f[n*m-i]=g[n*m+i]*fac[n*m-i]%mo; sort(a+1,a+n*m+1); int ans=0; for(int i=1,j;i<=n*m;i=j){ j=i+1; while(j<=n*m&&a[i]==a[j])++j; red(ans+=f[i-1]*(a[i]-a[i-1])%mo); } printf("%lld\n",ans); return; } signed main(){ fac[0]=1; for(int i=1;i<N;++i)fac[i]=fac[i-1]*i%mo; ifac[N-1]=qpow(fac[N-1],mo-2); for(int i=N-1;i;--i)ifac[i-1]=ifac[i]*i%mo; for(int cas=read();cas--;)solve(); return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!