Combinatorics
[ICPC 2024 Nanjing R] Bingo
先给序列排序,权值相同的钦定标号前的更小。转化成 \(Ans\le a_k\) 的情况,等价于 \(k\) 个 \(1\),\(nm-k\) 个 \(0\) 放入 \(n\times m\) 的矩阵,至少有一行或者一列是全 \(1\)。考虑其反面,钦定共 \(i\) 行 \(j\) 列都是 \(1\) 然后容斥,那么有:
\[f(k)=k!(nm-k)!\sum_{i=0}^n\sum_{j=0}^m(-1)^{i+j}\binom ni\binom mj\binom{nm-nj-im+ij}{k-nj-im+ij}
\]
令 \(s=nj+im-ij\),则 \(\binom{nm-nj-im+ij}{k-nj-im+ij}=\binom{nm-s}{k-s}=(nm-s)!/\left((k-s)!(nm-k)!\right)\)
令 \(g(s)=\sum_{i=0}^n\sum_{j=0}^m(-1)^{i+j}\binom ni\binom mj(nm-s)!\),\(h(s)=1/s!\),那么 \(f(k)=k!\sum_s g(s)h(k-s)\),卷积即可求出所有的 \(f(k)\),答案即:
\[\sum_{i=1}^{nm}(f(i)-f(i-1))a_i
\]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define mp make_pair
#define pb push_back
#define fi first
#define se second
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
const int N=8e5+10,mod=998244353,G=3,I=332748118,maxn=2e5;
void Add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
void Sub(int &x,int y){x-=y;if(x<0)x+=mod;}
int qpow(int a,int n)
{
int ans=1;
while(n)
{
if(n&1)ans=1ll*a*ans%mod;
a=1ll*a*a%mod;
n>>=1;
}
return ans;
}
namespace poly
{
int r[N];
void ntt(vector<int> &a,int lim,int k)
{
for(int i=0;i<lim;i++)if(i<r[i])swap(a[i],a[r[i]]);
for(int mid=1;mid<lim;mid<<=1)
{
int wn=qpow(k?G:I,(mod-1)/(mid<<1));
for(int R=mid<<1,j=0;j<lim;j+=R)
{
int w=1;
for(int t=0;t<mid;t++,w=1ll*w*wn%mod)
{
int x=a[j+t],y=1ll*w*a[j+mid+t]%mod;
a[j+t]=(x+y)%mod,a[j+mid+t]=(x-y+mod)%mod;
}
}
}
}
vector<int> mul(vector<int> f,vector<int> g)
{
int n=f.size()-1,m=g.size()-1,lim=1,l=0;
while(lim<=n+m)lim<<=1,l++;
for(int i=0;i<lim;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
f.resize(lim),g.resize(lim);
ntt(f,lim,1),ntt(g,lim,1);
vector<int> h(lim);
for(int i=0;i<lim;i++)h[i]=1ll*f[i]*g[i]%mod;
ntt(h,lim,0);int inv=qpow(lim,mod-2);
for(int i=0;i<=lim;i++)h[i]=1ll*h[i]*inv%mod;
while(!h.empty())
{
if(!*--h.end())h.pop_back();
else break;
}
return h;
}
}
using poly::mul;
int a[N],g[N],fac[N],ifac[N];
int binom(int n,int m){return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;}
void sol()
{
int n=read(),m=read();
for(int i=1;i<=n*m;i++)a[i]=read();
sort(a+1,a+n*m+1);
vector<int> f(n*m+1),g(n*m+1);
for(int i=0;i<=n;i++)for(int j=0;j<=m;j++)
{
int s=n*j+i*m-i*j;
if((i+j)&1)Sub(g[s],1ll*binom(n,i)*binom(m,j)%mod*fac[n*m-s]%mod);
else Add(g[s],1ll*binom(n,i)*binom(m,j)%mod*fac[n*m-s]%mod);
}
for(int i=0;i<=n*m;i++)f[i]=ifac[i];
f=mul(f,g);
for(int i=0;i<=n*m;i++)f[i]=1ll*f[i]*ifac[n*m-i]%mod*fac[i]%mod*fac[n*m-i]%mod;
for(int i=0;i<=n*m;i++)f[i]=(fac[n*m]-f[i]+mod)%mod;
for(int i=n*m;i>=1;i--)Sub(f[i],f[i-1]);
int Ans=0;
for(int i=1;i<=n*m;i++)Add(Ans,1ll*f[i]*a[i]%mod);
printf("%d\n",Ans);
}
int main()
{
fac[0]=1;for(int i=1;i<=maxn;i++)fac[i]=1ll*fac[i-1]*i%mod;
ifac[maxn]=qpow(fac[maxn],mod-2);for(int i=maxn-1;i>=0;i--)ifac[i]=1ll*(i+1)*ifac[i+1]%mod;
int T=read();
while(T--)sol();
return 0;
}