洛谷 AT_arc107_c [ARC107C] Shuffle Permutation 题解
观察发现,两种操作是不会互相影响的。做了一次行交换之后,再做列交换时两列的任何对应数的和不变。所以我们考虑分别计算行交换和列交换的方案数再相乘。
下面以行为例讲解,列是同理的。可以二重循环枚举可以交换的两行,再一重循环判断是否可以交换。我们知道,当第 \(i\) 行和第 \(j\) 行可以交换,第 \(j\) 行和第 \(k\) 行可以交换时,第 \(i\) 行和第 \(k\) 行也可以交换。为了保证答案不重不漏,可以把一些所有能够互通的的行放到一个集合里,于是我们可以使用并查集来维护,最后计算方案数。
时间复杂度 \(O(n^3)\),记得做完行之后清空数组再计算列。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=510;
const int MOD=998244353;
int n,m,a[N][N],fa[N],cnt[N];
ll fac[N]={0,1},ans=1;
bool check_hang(int x,int y)
{
for(int i=1;i<=n;i++)
{
if(a[x][i]+a[y][i]>m) return false;
}
return true;
}
bool check_lie(int x,int y)
{
for(int i=1;i<=n;i++)
{
if(a[i][x]+a[i][y]>m) return false;
}
return true;
}
int set_find(int x)
{
return x==fa[x]?x:fa[x]=set_find(fa[x]);
}
void set_merge(int x,int y)
{
int gx=set_find(x),gy=set_find(y);
if(gx!=gy) fa[gx]=gy;
}
int main()
{
memset(a,0x3f,sizeof a);
scanf("%d%d",&n,&m);
for(int i=2;i<=500;i++) fac[i]=(fac[i-1]*i)%MOD;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) scanf("%d",&a[i][j]);
}
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(check_hang(i,j)) set_merge(i,j);
}
}
for(int i=1;i<=n;i++) cnt[set_find(i)]++;
for(int i=1;i<=n;i++)
{
if(cnt[i]) ans=(ans*fac[cnt[i]])%MOD;
}
for(int i=1;i<=n;i++) fa[i]=i,cnt[i]=0;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(check_lie(i,j)) set_merge(i,j);
}
}
for(int i=1;i<=n;i++) cnt[set_find(i)]++;
for(int i=1;i<=n;i++)
{
if(cnt[i]) ans=(ans*fac[cnt[i]])%MOD;
}
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号