Scheming Furry 2023牛客暑期多校训练营8 K

题目大意:给出一个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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值