C - Shuffle Permutation
这几天遇到了很多(2道)并查集维护连通关系的题。
此题把能够相互交换的行或者列用并查集维护,不难发现一个连通块内的点个数时 c n t cnt cnt连通块内的行或者列可以两两交换,那么对答案的贡献是 c n t ! cnt! cnt!因此预处理,然后用并查集维护连通关系即可。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<random>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=60;
const int mod=998244353;
int g[N][N];
int n,m;
int p[2*N],sz[2*N];
int find(int x) {return x==p[x]?x:p[x]=find(p[x]);}
int fact[2*N];
void merge(int x,int y)
{
int px=find(x),py=find(y);
if(px==py) return;
p[px]=py;
sz[py]+=sz[px];
}
int main()
{
//IO;
int T=1;
//cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) cin>>g[i][j];
fact[0]=1;
for(int i=1;i<=2*n;i++) p[i]=i,sz[i]=1,fact[i]=1ll*fact[i-1]*i%mod;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
bool ok1=1,ok2=1;
for(int k=1;k<=n;k++)
if(g[i][k]+g[j][k]>m) ok1=0;
for(int k=1;k<=n;k++)
if(g[k][i]+g[k][j]>m) ok2=0;
if(ok1) merge(i,j);
if(ok2) merge(i+n,j+n);
}
ll res=1;
for(int i=1;i<=2*n;i++)
if(p[i]==i)
res=res*fact[sz[i]]%mod;
cout<<res<<'\n';
}
return 0;
}