[ AtCoder Regular Contest 107 ] C

Problem

题目链接

Solution

性质1: 交换行对后续的交换列没有影响,反之同理。

性质2: 如果 (x,y) 行可以交换, (y,z) 行可以交换,那么 (x,y,z) 就可以互相交换并且造成 (3!) 的贡献,进一步可以推广为若干行之间交换的贡献。列同理。

根据 性质1 和 性质2,我们可以用并查集维护可以互相交换的行、列集合大小,每个可以互相交换的集合贡献就是集合元素的全排列个数各个贡献相乘就是答案

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
    return x * f;
}
const int N = 57, mod = 998244353;
int n,K;
int a[N][N],fa[N<<1],sz[N<<1],alp[N];
bool vis[N<<1];
int Find(int x) {
	return (x==fa[x] ? x : fa[x] = Find(fa[x]));
}
void Join(int x,int y) {
	int fx = Find(x), fy = Find(y);
	if(fx != fy) {
		fa[fx] = fy, sz[fy] += sz[fx];
	}
}
signed main()
{
	n = read(), K = read();
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j) a[i][j] = read();
	for(int i=1;i<=2*n;++i) fa[i] = i, sz[i] = 1;
	alp[0] = 1;
	for(int i=1;i<=n;++i) alp[i] = (alp[i-1] * i) % mod;
	for(int x=1;x<=n;++x) {
		for(int y=1;y<=n;++y) {
			bool flag1 = 1, flag2 = 1;
			for(int i=1;i<=n;++i) {
				flag1 &= (a[x][i] + a[y][i] <= K);
				flag2 &= (a[i][x] + a[i][y] <= K);
			}
			if(flag1) Join(x, y);
			if(flag2) Join(x+n, y+n);
		}
	}
	int ans = 1;
	for(int i=1;i<=2*n;++i) {
		int fx = Find(i);
		if(!vis[fx]) {
			vis[fx] = 1; ans = (ans * alp[sz[fx]]) % mod;
		}
	}
	printf("%lld
",ans);
    return 0;
}
/*
3 13
3 2 7
4 8 9
1 6 5

12
*/

Summary

  • 对于一个集合内元素可以互相交换,贡献即全排列
原文地址:https://www.cnblogs.com/BaseAI/p/13914165.html