Description
一个\(n \times m\)的滑块游戏,从左到右从上到下依次编号\(1\)到\(n \times m-1\),最后一块是空的,现在把\(n \times m-1\)个滑块拿出来按编号升序排,每次把第\(1,p+1,2p+1, \dots ,np+1\)块拿出来从左到右从上到下放好,问放好后的局面是否可以恢复成编号为\(i\)的滑块在第\(i\)号位置的局面。
Input
第一行一整数\(T\)表示用例组数,每组用例输入三个整数\(n\),\(m\),\(p\)。\(T \leqslant 100,2\leqslant n,m\leqslant 1000,1\leqslant p\leqslant n \times m-2\)。
Output
对于每组用例,如果可以恢复成初始局面则输出YES,否则输出NO。
Sample Input
3
3 2 3
3 2 4
999 999 1
Sample Output
YES
NO
YES
Solution
结论一:相邻的两个数字块交换位置改变序列逆序对数量的奇偶性。
结论二:白块左右移动不会改变逆序对的数量。
结论三:白块上下移动最终不会改变逆序对数量的奇偶性。因为白块每向上或向下移动一位相当于对应的数字块向下或向上移动\(m-1\)次,而白块一定是从右下角最终回到右下角,移动偶数次,数字块也移动偶数次,不会改变逆序对数量的奇偶性。
结论四:任何规模的拼图最终一定会成为只有右下角\(2\times 2\)的方格不确定的情况,枚举可能的\(6\)种情况,发现逆序对数量奇偶性相同的情况可以互达。
结论五:由以上结论可知,任何局面一定可以转化为右下角\(2\times 2\)方格不确定的情况而逆序对数量奇偶性不变,而最后有序情况的逆序对数量为\(0\),因此只需判断最开始情况逆序对的数量,若为偶数就可以复原,反之不能复原。
求逆序对时,可以累加每个数字后面比它小的数字的个数,即累加每个数字对逆序对的贡献。设当前轮剩余的数字数量为\(tot\),则这一轮可以取到的数字个数为\((tot - 1) / p + 1\),其中除第一个数不贡献逆序对数之外,其余的\(num=(tot - 1) / p\)个数每个数贡献的逆序对数量构成首项为\(p-1\),公差为\(p-1\)的等差数列,求和化简后的公式为\(num * (num + 1) / 2 * (p - 1)\)。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e3 + 10;
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
int n, m, p;
scanf("%d%d%d", &n, &m, &p);
int tot = n * m - 1;
ll ans = 0;
while (tot)
{
int num = (tot - 1) / p;
ans += num * (num + 1) / 2 * (p - 1);
tot -= num + 1;
}
printf("%s\n", ans & 1 ? "NO" : "YES");
}
return 0;
}