矩阵翻硬币 (BigInteger+大数开方)

 历届试题 矩阵翻硬币  
时间限制:1.0s   内存限制:256.0MB
         
问题描述
  小明先把硬币摆成了一个 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次方)。


code:

题目解析:  

  1、从题目得知,如果一个硬币被翻转了奇数次后为正面朝上,那么它原始的状态一定是反面朝上。因此,我们需要统计所有翻转了奇数次硬币的个数。

  2、题目中的 “ 对第x行第y列的硬币进行 Q 操作的定义:将所有第 i*x 行,第 j*y 列的硬币进行翻转 ”。我们可以试着逆向思索,对于一个横坐标为X的硬币而言,我们翻转哪些硬币会影响到它而使它翻转呢?由此我们可以得出,当翻转的硬币的横坐标为X的约数时,会影响到它的翻转。比如,X=9,那么翻转横坐标为 1、3、9的时候会影响到它的翻转。纵坐标情况同理。

    对于一个硬币,我们必须考虑到它的横坐标和纵坐标。假如,此硬币的横坐标翻转了5次,纵坐标翻转了6次,那么它总的翻转次数为 5 * 6 = 30 次。因此我们得到一个公式:总翻转次数 (count) = 横坐标翻转次数 (count_x) * 纵坐标翻转次数 (count_y)。我们一开始就指出,我们需要找到翻转了奇数次的硬币,因此,横坐标翻转次数和纵坐标翻转次数均为奇数时,总翻转次数才为奇数次。

  3、接下来我们需要考虑,哪些数有奇数个约数呢?答案是完全平方数。它为 1,4,9,16,25,36...... 即n的2次方,n为从1开始的正整数。

    从题目中得知,此时是一个 n * m 的矩阵,行号和列号都是从1开始。因此我们需要解决 1 到 n 之间完全平方数个数的问题。方法是求出sqrt(n),然后对它取整,即 1 - n 之间总共有 (int)(sqrt(n)) 个完全平方数。因此,反面朝上硬币的个数为横纵坐标完全平方数个数相乘,即 (int)((sqrt(n)) * (sqrt(m))) 。

大数开方二分法逼近
import java.math.*;
import java.util.Scanner;
public class Main {
   static BigInteger cal(BigInteger x){
	   BigInteger l = BigInteger.ONE;
	   BigInteger r = x;
	   BigInteger mid = BigInteger.ZERO;
	   while(r.compareTo(l) == 1 || r.compareTo(l) == 0){
		   mid = l.add(r).divide(BigInteger.valueOf(2));   
		   if(mid.multiply(mid).compareTo(x) == 1)
			   r = mid.subtract(BigInteger.ONE);
		   else
			   l = mid.add(BigInteger.ONE);
	   }
	   if(r.multiply(r).compareTo(x) == 1)
		   r.subtract(BigInteger.ONE);
	   return r;
   }
	public static void main(String[] args) {
		// TODO Auto-generated method stub
        BigInteger n;
        BigInteger m;
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextBigInteger();
        m = scanner.nextBigInteger();
        BigInteger x = cal(n);
        BigInteger y = cal(m);
        System.out.println(x.multiply(y));
	}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaBigInteger类是用于处理大数的类。它可以处理比long类型更大的整数。BigInteger类的原理是使用一个int类型的数组来存储大数的每个位。数组的每个元素都是大数的一部分。这个数组被称为mag[]。 1. 按位取反:BigInteger类提供了not()方法来实现按位取反的操作。该方法首先创建一个与原始BigInteger对象相同长度的int数组result[]。然后,通过循环遍历result[]数组,将mag[]数组的每个元素取反并存储到result[]数组。最后,使用valueOf()方法将result[]数组转换为一个新的BigInteger对象并返回。 示例代码如下: ```java public BigInteger not() { int[] result = new int[intLength()]; for (int i = 0; i < result.length; i++) { result[i] = ~getInt(result.length - i - 1); } return valueOf(result); } ``` 2. 按位与操作:BigInteger类提供了and()方法来实现按位与操作。该方法首先创建一个长度为两个BigInteger对象较大的int数组result[]。然后,通过循环遍历result[]数组,将mag[]数组和另一个BigInteger对象的mag[]数组对应位置的元素进行按位与操作,并将结果存储到result[]数组。最后,使用valueOf()方法将result[]数组转换为一个新的BigInteger对象并返回。 示例代码如下: ```java public BigInteger and(BigInteger val) { int[] result = new int[Math.max(intLength(), val.intLength())]; for (int i = 0; i < result.length; i++) { result[i] = (getInt(result.length - i - 1) & val.getInt(result.length - i - 1)); } return valueOf(result); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值