C - Shuffle Permutation
这几天遇到了很多(2道)并查集维护连通关系的题。
此题把能够相互交换的行或者列用并查集维护,不难发现一个连通块内的点个数时cntcntcnt连通块内的行或者列可以两两交换,那么对答案的贡献是cnt!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;
}