问题描述
小明先把硬币摆成了一个 n 行 m 列的矩阵。
随后,小明对每一个硬币分别进行一次 Q 操作。
对第x行第y列的硬币进行 Q 操作的定义:将所有第 i*x 行,第 j*y 列的硬币进行翻转。
其中i和j为任意使操作可行的正整数,行号和列号都是从1开始。
当小明对所有硬币都进行了一次 Q 操作后,他发现了一个奇迹——所有硬币均为正面朝上。
小明想知道最开始有多少枚硬币是反面朝上的。于是,他向他的好朋友小M寻求帮助。
聪明的小M告诉小明,只需要对所有硬币再进行一次Q操作,即可恢复到最开始的状态。然而小明很懒,不愿意照做。于是小明希望你给出他更好的方法。帮他计算出答案。
随后,小明对每一个硬币分别进行一次 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次方)。
对于20%的数据,n、m <= 10^7;
对于40%的数据,n、m <= 10^15;
对于10%的数据,n、m <= 10^1000(10的1000次方)。
这道题用java做是比较简单的,因为数据特别大,用BigInteger做就简单了。
一开始没有读懂题意,这是最坑的,题意是对每一个点都做Q操作,对点(x,y)做Q操作,就是说对所有满足条件的点(X,Y)都进行一次翻转,条件就是X = i * x,Y = j * y,(i,j为正整数),我们需要直到每个点经过了几次翻转,如果是奇数次就是反面朝上,这个很好想,然后考虑怎么计算翻转次数,题目数据很大,肯定不能排着看每个点,这个要搞清楚,可以发现x是X的因数,y是Y的因数,只有横坐标纵坐标都对应是自己的因数的点才会触发自己的翻转,那么一共有多少个点,假设横坐标有p个因数,纵坐标有q个因数,综合成p*q个点,也就是进行了p*q个翻转操作,p*q是奇数当且仅当p和q都是奇数,那么什么样的数的约数是奇数个,我们在找一个数n的因数(包括1和本身)时,一般是这样的:
c=0;
for i=(1 -> n):
if(n % i == 0):
if(n / i != i) c += 2;//成对
else c += 1;//单个
可以看出只有平方数才有奇数个因数,那么我们只需要直到横坐标都有多少个平方数,纵坐标有多少个平方数即可知道共有多少个横纵坐标都是平方数的点,即翻转奇数次的点。
对于正整数n,不大于n的平方数一共有[n0.5]个,即sqrt(n)(返回整数),java为BigInteger编写一个Sqrt函数,答案就显而易见了。
因为BigInteger类没有这个函数,所以我们需要自己写,以下是我的编写思路,不知道是否正确,提交答案是对了:
对于给定的n,bitLength求出它的的二进制位数c,那么它的根的二进制位数一定是在a = c / 2和b = c / 2 + 1之间,后面要算移位,所以这里c要减1,即a = (c + 1) / 2和b = (c + 1) / 2 + 1,然后在1 << a和1 << b之间做二分。
代码:
import java.math.BigInteger; import java.util.Scanner; public class Main { private static Scanner sc = new Scanner(System.in); private static BigInteger Sqrt(BigInteger n) { BigInteger l = BigInteger.ZERO; BigInteger r = BigInteger.ONE.shiftLeft((n.bitLength() + 1) / 2); while(l.compareTo(r) < 0) { BigInteger mid = l.add(r.add(BigInteger.ONE)).shiftRight(1); if(mid.multiply(mid).compareTo(n) > 0) r = mid.subtract(BigInteger.ONE); else l = mid; } return l; } public static void main(String[] args) { BigInteger n = sc.nextBigInteger(); BigInteger m = sc.nextBigInteger(); System.out.println(Sqrt(n).multiply(Sqrt(m))); } }
用c做就好麻烦,乘法要自己写,没用二分。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; char n[2000],m[2000]; const char *Mul(const char * a,const char * b); const char *Sqrt(const char * a); int Comp(const char * a,const char *b); int main() { scanf("%s%s",n,m); printf("%s",Mul(Sqrt(n),Sqrt(m))); } const char *Mul(const char * a,const char * b) { int alen = strlen(a),blen = strlen(b); char *ans = new char[alen + blen + 1]; int d = 0,i,j; int len = 0; for(i = 0;i < alen;i ++) { for(j = 0;j < blen;j ++) { d += (a[alen - 1 - i] - '0') * (b[blen - 1 - j] - '0'); if(i + j < len) { d += ans[i + j] - '0'; } else len ++; ans[i + j] = d % 10 + '0'; d /= 10; } len = i + j; while(d) { ans[len ++] = d % 10 + '0'; d /= 10; } } ans[len] = 0; reverse(ans,ans + len); return ans; } int Comp(const char * a,const char *b) { int c = strlen(a) - strlen(b); if(c) return c < 0 ? -1 : 1; return strcmp(a,b); } const char *Sqrt(const char * a) { int len = (strlen(a) + 1) / 2; char *ans = new char[len + 1]; for(int i = 0;i < len;i ++) { ans[i] = '0'; } ans[len] = 0; for(int i = 0;i < len;i ++) { while(Comp(Mul(ans,ans),a) <= 0 && ans[i] <= '9') ans[i] ++; ans[i] --; } return ans; }
import java.math.BigInteger;import java.util.Scanner;
public class Main {private static Scanner sc = new Scanner(System.in);private static BigInteger Sqrt(BigInteger n) {BigInteger l = BigInteger.ZERO;BigInteger r = BigInteger.ONE.shiftLeft((n.bitLength() + 1) / 2);while(l.compareTo(r) < 0) {BigInteger mid = l.add(r.add(BigInteger.ONE)).shiftRight(1);if(mid.multiply(mid).compareTo(n) > 0) r = mid.subtract(BigInteger.ONE);else l = mid;}return l;}public static void main(String[] args) {BigInteger n = sc.nextBigInteger();BigInteger m = sc.nextBigInteger();System.out.println(Sqrt(n).multiply(Sqrt(m)));}}