题解:P13776 「o.OI R2」Easy ver.
先说赛时思路,后面是结论和证明,前面可以跳过。
赛时思路
题目的“连通块”有点抽象,其实就是连通的一块啦,和权值无关。
先分析样例。
第一个样例是一行八列,那么连通块必然是一个子段。所以假设从左往右是
根据异或的性质,我们有
第二个样例是
a b c
d e f
g h i
选择
那么用
而所有数字都一样,
样例
这些故事样例告诉我们一些道理结论:
-
- 虽然没说但是考虑一下
n\times m=k 答案是2^{n\times m-1} ,自证不难;以下讨论都不包含上面的两种情况。 -
-
如果最后的结论是正确的那么这题就做完了!赛时想了想挺对的,感觉证明挺麻烦就交上去过了(WA 了一发,因为没开 long long)。但是我们要在这里进行一个证明(赛时没证)。
证明
证明的思路是,证明在某个区域内所有数字都相同。第一个思路是模仿样例
2 ,考虑3\times t ,实际上这样并不是很行,因为t 可能大于n 。但是其实这样做是有普适性的,所以我们不必使用3 这个数字,我们可以使用n\times t ,不管先要证明t\ge 2 ,不然不好做。很简单但是直接做会有问题,k=1 证不了全相同,但是可以证明全是0 。
不妨设
然后的思路是,填满它。
若 X。把其中一个替换为 O,然后在它右边紧贴着的地方填一个 U(因为 U):
XXXXXXXXX
XXXXXXXXX
XXXXXXXX
XXXOXXXX
XXXXXXXXU
XXXXXXXX
X+O 是连通的,X+U 也是连通的,所以 O 和 U 不相邻就可以这么干(有一种特殊情况就是相邻也可以这么干但是我们不用管它)。所以选定一个 U 我们就能够证明长方形的所有部分除了和 U 直接相连的那个格子之外都是一样的。又 X 都相同。
这样我们知道了所有行中数字都是一样的,相应地交换
代码实现&提交记录
可可爱爱的赛时代码。
/*
样例 1:
????????
k=6
那么
123456
234567
345678
xor=0
也就是说
1=7
2=8
那么自由度为 6
然而给定了 12345 就能推断出 6,7,8
所以自由度为 5
输出 32
样例 2:
?????
?????
?????
?????
?????
k=5
可以推断出所有数字均相同
而 k 为奇数
所以是 1
样例 3:
看起来很唬人,实际上 114*514<1919810
所以是 2^(114*514) mod 1e9+7
那么?
不妨假设 nm>=k,n<=m
若 nm=k:答案为 2^(nm-1)
若 n=1:长条形,这样答案是容易计算的,容易发现 1&k+1,2&k+2,... 是相同的,然后 k 也不是自由的,所以答案为 2^(k-1)。
若 n>1:应该是可以推出所有数字相同的,那么 k 为奇数的时候是 1,偶数是 2
嗯我猜结论是对的。
*/
/*
好吧,炸了,6。
不过 subtask 0,1,2 是对的
3,4 不对
说明 n=1 讨论是对的
nm=k 草忘了写了
还有没开 long long
wssb
*/
#include <cstdio>
#include <algorithm>
using namespace std;
long long qpow(long long x, long long y)
{
if(y == 0) return 1;
if(y == 1) return x % 1000000007;
long long res = qpow(x, y >> 1);
res = res * res % 1000000007;
if(y & 1) res = res * x % 1000000007;
return res;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
if(n > m) swap(n, m);
if(1ll*n*m < k) printf("%lld\n", qpow(2, 1ll*n*m));
else if(1ll*n*m == k) printf("%lld\n", qpow(2, 1ll*n*m-1));
else if(n == 1) printf("%lld\n", qpow(2, k-1));
else printf("%lld\n", (k & 1) ? 1 : 2);
}
return 0;
}
可可爱爱的赛时提交记录。