题目大意:给出一个n*m的排列矩阵,A先手,B后手,A每次操作要交换两行的数,B每次操作要交换两列的数,问谁能在自己最后一次操作后使整个矩阵变成递增的,如果某人知道自己赢不了,他也不会让对手赢。
2<=n,m<=200;1<=t<=100
思路:首先判断当前的矩阵能不能变成有序的,因为他们只能整行整列的换,所以要想最后变成有序的排列,必须每一行都是一个排列,且数字范围为[xm+1,(x+1)m](可以从最终有序的状态到退回去想),那么我们可以将每一行的数字取出来排个序,每个数必须相差1,且第一个数满足%m=1,每行之间每个数的差都应该相等且等于m的倍数,满足这两个条件才有可能最终变为递增排列矩阵
因为如果自己赢不了也不让对方赢,所以当min(n,m)>2时,除非先手第一步就能赢,否则在另一个人差一步排序好时,另一个人总有办法把顺序打乱不让他赢,所以没有解,当n=2的时候,A只能不停的交换唯一的这两行,那么主动权在B手里,B肯定不会让A赢的,又因为B是后手,所以要满足将列变为有序所需的操作数+将行变为有序的操作数是偶数才行,这样才能使的在他行动时正好能把矩阵变成递增,然后我们又发现无论他以怎样的顺序操作,所需要的操作次数的奇偶性是相同的,那么我们dfs一下置换环求最小操作次数即可。
m=2时,与上面类似,A肯定不会让B赢,只要行列最小操作数之和是奇数,就能在轮到他时将矩阵变为递增,当n=2且m=2时,这时只有三种合法情况,讨论一下即可
#include<bits/stdc++.h>
//#include<__msvc_all_public_headers.hpp>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const ll MOD = 998244353;
const int INF = 0x7fffffff;
int n;
int m;
int vis1[N];
int vis2[N];
void init()
{
for (int i = 1; i <= max(n,m); i++)
{
vis1[i] = 0;
vis2[i] = 0;
}
}
int a[205][205];
int e1[N], e2[N];
void dfs1(int u)
{//遍历环
if (vis1[u])
return;
vis1[u] = 1;
dfs1(e1[u]);
}
void dfs2(int u)
{//遍历环
if (vis2[u])
return;
vis2[u] = 1;
dfs2(e2[u]);
}
void solve()
{
cin >> n >> m;
init();
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> a[i][j];
}
}
for (int i = 1; i <= n; i++)
{
e1[i] = (a[i][1] - 1) / m + 1;//对行建立置换环
vector<int>temp;
for (int j = 1; j <= m; j++)
{
if (i == 1)
{
e2[j] = a[i][j] % m;//对列建立置换环
if (a[i][j] % m == 0)
e2[j] = m;
}
temp.push_back(a[i][j]);
if (i > 1)
{
if (!(a[i][j] - a[i - 1][j] == a[i][1] - a[i - 1][1] && (a[i][j] - a[i - 1][j]) % m == 0))//相邻行的数的差相等且等于m的倍数
{
cout << "NSFW" << endl;
return;
}
}
}
sort(temp.begin(), temp.end());
for (int i = 1; i < temp.size(); i++)
{//每一行的数排序之后应该是一个m的倍数的排列
if (!(temp[i] == temp[i-1] + 1 && temp[0] % m == 1))
{
cout << "NSFW" << endl;
return;
}
}
}
int ans1 = 0;
for (int i = 1; i <= n; i++)
{
if (!vis1[i])
{
dfs1(i);
ans1++;
}
}
ans1 = n - ans1;//将所有行变成递增需要的最小操作数
int ans2 = 0;
for (int i = 1; i <= m; i++)
{
if (!vis2[i])
{
dfs2(i);
ans2++;
}
}
ans2 = m - ans2;//将所有列变成递增需要的最小操作数
if (n == 2 && m == 2)
{//这时两个人都有机会赢
if ((ans1 + ans2) % 2 == 0)
{
cout << "CAT" << endl;
return;
}
if ((ans1 + ans2) % 2 == 1)
{
cout << "FOX" << endl;
return;
}
}
if (ans1 == 1 && ans2 == 0)
{//先手一步直接赢
cout << "FOX" << endl;
return;
}
if (n > 2 && m > 2)
{//没人会让对面赢
cout << "NSFW" << endl;
return;
}
if (n == 2)
{//后手有机会赢
if ((ans1 + ans2) % 2 == 0)
{
cout << "CAT" << endl;
return;
}
else
{
cout << "NSFW" << endl;
return;
}
}
if (m == 2)
{//先手有机会赢
if ((ans1 + ans2) % 2)
{
cout << "FOX" << endl;
return;
}
else
{
cout << "NSFW" << endl;
return;
}
}
}
int main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(false);
int t;
cin>>t;
while (t--)
{
solve();
}
return 0;
}