蓝桥杯 矩阵翻硬币
矩阵翻硬币
本文转自 https://blog.csdn.net/xiaofengcanyuelong/article/details/79255105
小明先把硬币摆成了一个 n 行 m 列的矩阵。
随后,小明对每一个硬币分别进行一次 Q 操作。
对第x行第y列的硬币进行 Q 操作的定义:将所有第 i*x 行,第 j*y 列的硬币进行翻转。
其中i和j为任意使操作可行的正整数,行号和列号都是从1开始。
当小明对所有硬币都进行了一次 Q 操作后,他发现了一个奇迹——所有硬币均为正面朝上。
小明想知道最开始有多少枚硬币是反面朝上的。于是,他向他的好朋友小M寻求帮助。
聪明的小M告诉小明,只需要对所有硬币再进行一次Q操作,即可恢复到最开始的状态。然而小明很懒,不愿意照做。于是小明希望你给出他更好的方法。帮他计算出答案。
【数据格式】
输入数据包含一行,两个正整数 n m,含义见题目描述。
输出一个正整数,表示最开始有多少枚硬币是反面朝上的。
【样例输入】
2 3
【样例输出】
1
【数据规模】
对于10%的数据,n、m <= 10^3;
对于20%的数据,n、m <= 10^7;
对于40%的数据,n、m <= 10^15;
对于10%的数据,n、m <= 10^1000(10的1000次方)。
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
题解:
Q 操作的定义:将所有第 i*x 行,第 j*y 列的硬币进行翻转。
(一)、找规律。
1、先看1行m列的矩阵,因为只有1行,当对其进行Q操作的时候,i和x都只能为1,
假设m为5,在对1行5列的矩阵的每个元素进行了Q操作后,推导过程如下:
初始状态:1 1 1 1 1
对(1,1)元素进行Q操作,此时变成0 0 0 0 0
对(1,2)元素进行Q操作,此时变成0 1 0 1 0
对(1,3)元素进行Q操作,此时变成0 1 1 1 0
对(1,4)元素进行Q操作,此时变成0 1 1 0 0
对(1,5)元素进行Q操作,此时变成0 1 1 0 1
综上,可以看出,这五个元素分别翻转了1,2,2,3,2次,很明显,当进行奇数次翻转后为0,当进行偶数次翻转后变为1。那么,我们如果要求最终有几个0,便是求有几个位置可以进行奇数次翻转。
首先,设1<=x<=m,对于(1,m)矩阵中的(1,x)硬币,它翻转了奇数次呢?还是偶数次呢?这时回看到Q操作的定义,会发现这样一个规律:(1,x)位置翻转的次数等于x的约数个数,且当x=k^2(k=1,2,3,4,5,6)时,翻转次数为奇数次,否则为偶数次。
如1行5列矩阵中:
1的约数只有1个,因此(1,1)翻转了1次,
2的约数有2个,因此(1,2)翻转了2次,
3的约数有2个,因此(1,3)翻转了2次,
4的约数有3个,因此(1,4)翻转了3次,
5的约数有2个,因此(1,5)翻转了2次。
当k=1,2时,即x=1,4时翻转奇数次,其他为偶数次。
综上,一个(1,m)矩阵在对每一个元素都进行Q操作后,0的个数为根号下m。
2、那么(n,m)矩阵呢?这里不再赘述,找个简单的矩阵再走一遍流程就会得到下面的结论。
一个(n,m)矩阵在对每一个元素都进行Q操作后,0的个为根号下n乘以根号下m
参考:http://blog.csdn.net/snailset/article/details/26752435
(二)、处理大数开根。
对于100%的数据,n、m <= 10^1000(10的1000次方)。
n,m的最大值是10^1000次方,那么普通的开根已经不适用于这种情况了,在Java中有BigInteger和BigDecimal可以满足这样大数开根的需求。
参考:https://www.cnblogs.com/Annetree/p/6664383.html
最后得出以下代码:
1 public class Demo {
2
3 public static void main(String[] args) {
4
5 Scanner scanner = new Scanner(System.in);
6 BigInteger n = scanner.nextBigInteger();
7 BigInteger m = scanner.nextBigInteger();
8 BigInteger tem = sqrt(n).multiply(sqrt(m));
9 System.out.println(tem);
10
11 }
12 //大数开根--->折半查找法
13 static BigInteger sqrt(BigInteger x) {
14 BigInteger l = BigInteger.ONE;
15 BigInteger r = x;
16 BigInteger temp = BigInteger.ZERO;// 0
17 while (!l.equals(r)) {
18 BigInteger mid = (l.add(r)).divide(BigInteger.valueOf(2));// (l+r)/2
19 // temp!=0&&temp==mid---->mid!=0
20 if (temp.compareTo(BigInteger.ZERO) != 0 && temp.compareTo(mid) == 0) {
21 break;
22 } else {
23 temp = mid;
24 }
25 if (temp.compareTo(BigInteger.ZERO) == 0) {
26 temp = mid;
27 }
28 // mid*mid>x
29 if (mid.multiply(mid).compareTo(x) == 1) {
30 r = mid;
31 } else {
32 l = mid;
33 }
34 }
35 if (l.multiply(l).compareTo(x) == 1) {
36 l = l.subtract(BigInteger.ONE);
37 }
38 return l;
39
40 }
41 }