10.矩阵翻硬币
- 小明先把硬币摆成了一个 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
思路
- 用题目中的操作只能过10%的数据.
- 首先要理解Q操作的定义:将所有
第i*x 行,第j*y
列的硬币进行翻转. 也就是说现在定位的硬币坐标是x,y, 是x的倍数的行都要翻, 是y的倍数的列也要翻. - 假设有9行9列的硬币, 对坐标(9,9)的硬币target进行分析. 第一行的硬币命中了target的行, 假如来到第一列, 那么target就要翻一下; 假如来到了第1行, 第3列, target也要翻一下.
- 这么一来, 一个硬币就要翻
(x的因子数 * y的因子数)
那么多次 - 如果一个数一共翻了奇数次, 它一开始就是反面朝上的.
- 由于只有奇数相乘才能得到奇数. 所以要求x的因子数和y的因子数都必须是奇数.
- 什么数的因子数是奇数? 只有平方数的因子数是奇数, 因为普通数的因子数都是一对一对的.
- 所以现在的问题转化成求
x中有多少个平方数
和y中有多少个平方数
. - 平方数有: 1, 4, 9…
- 根据数学规律, 求一个数包含多少个平方数只需要将该数开方后向下取整即可, 比如说10开方取整后得3, 它一共包含3个平方数.
- 由于数据的取值范围是10^1000, 不能用已有的基本数据类型进行开放, 所以接下来的问题转化为如何对一个大数进行开方.
- 首先要知道大数有多少位, 比如两位数16开方后得一位数4, 3位数100开方后得2位数10. 总结出结果是如果一个数的位数是偶数, 开方后得到的数的位数也是偶数, 如果是奇数, 得出的结果有
原数位数 / 2 + 1
这么多位. - 知道结果有多少位后, 就可以试数了. 假设结果是3位, 先假设为000, 然后从头开始试100, 试110, 试111, 试112…直到得出平方数.
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String n = sc.next();
String m = sc.next();
System.out.println(sqrt(n).multiply(sqrt(m)));
}
public static BigInteger sqrt(String num){
int numLen = num.length();//要开方的数字位数
int resLen = (numLen & 1) == 0 ? numLen / 2 : numLen / 2 + 1;//开方后数字的位数
char[] arr = new char[resLen];//用于尝试结果的数组
Arrays.fill(arr, '0');
BigInteger target = new BigInteger(num);
for(int pos = 0; pos < resLen; pos++){
for(char i = '1'; i <= '9'; i++){
arr[pos] = i;
BigInteger pow = new BigInteger(String.valueOf(arr)).pow(2);
if(pow.compareTo(target) == 1){
arr[pos] -= 1;
break;
}
}
}
return new BigInteger(String.valueOf(arr));
}
}