1. 程式人生 > >歷屆試題 矩陣翻硬幣 【java大數開方】

歷屆試題 矩陣翻硬幣 【java大數開方】

  歷屆試題 矩陣翻硬幣  

時間限制: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次方)。

思路:

模擬一遍可以發現結果是求被翻了奇數次的硬幣數。奇數=奇數*奇數。最終反面朝上的硬幣為翻轉奇數次的行與翻轉奇數次的列的交點。所以答案就是翻轉奇數次的行數*翻轉奇數次的列數。很容易發現第n行的翻轉次數=n的因子數。而因子一般都是成對出現的,只有平方數會出現奇數個因子。所以如果行數為平方數,就會被翻奇數次。判斷一個數之前有多少個平方數直接開方取整即可。

這裡用到了大數開方。java沒有專門的方法,可以用二分進行開方。

程式碼:


import java.util.*;
import java.math.*;

public class Main {

	// 大數開方
	static BigInteger Bigsqrt(BigInteger x) {
		BigInteger l = BigInteger.ONE;
		BigInteger r = x;
		BigInteger temp = BigInteger.ZERO;
		while (!l.equals(r)) {
			BigInteger mid = (l.add(r)).divide(BigInteger.valueOf(2));
			if (temp.compareTo(BigInteger.ZERO) != 0 && temp.compareTo(mid) == 0) break;
			else temp = mid;
			if (temp.compareTo(BigInteger.ZERO) == 0) temp = mid;
			if (mid.multiply(mid).compareTo(x) == 1) r = mid;
			else l = mid;
		}
		if (l.multiply(l).compareTo(x) == 1)
			l = l.subtract(BigInteger.ONE);
		return l;
	}

	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		BigInteger n = cin.nextBigInteger();
		BigInteger m = cin.nextBigInteger();
		System.out.println(Bigsqrt(n).multiply(Bigsqrt(m)));
	}

}