问题描述
小明先把硬币摆成了一个 n 行 m 列的矩阵。
随后,小明对每一个硬币分别进行一次 Q 操作。
对第x行第y列的硬币进行 Q 操作的定义:将所有第 ix 行,第 jy 列的硬币进行翻转。
其中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次方)。
这道题主要是找规律,然后需要高精度乘法的应用来开方.
//矩阵翻硬币
//主要用到高精度开方
//对于一个数 若他位数是偶数则他的开放数为len/2,若是奇数则为(len+2)/2
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int num1[1005]; //保存n,m的值
int num2[1005];
// 对高精度大数的比较大小
int Compare(int A[],int B[])
{
if(A[0]>B[0]) return 1;
else if(A[0]<B[0]) return -1;
else
{
for(int i = A[0];i>=1;i--)
{
if(A[i]>B[i]) return 1;
else if(A[i]<B[i]) return -1;
}
}
return 0;
}
//高精度乘法操作
void CF(int G[],int A[],int B[])
{
int T[2020];
memset(T,0,sizeof(T));
T[0]=A[0]+B[0];
for(int i=1;i<=A[0];i++)
{
for(int j = 1;j<=B[0];j++)
{
T[i+j-1]+=(A[i]*B[j]);
T[i+j]+=(T[i+j-1]/10);
T[i+j-1]%=10;
}
}
while(T[T[0]]==0&&T[0]>0) T[0]--;
for(int i =0;i<=T[0];i++)
G[i]=T[i];
return ;
}
void Sqrt(int A[]) //开方函数
{
int len = (A[0]+1)/2; //这是必定正确的
int T[2020];
memset(T,0,sizeof(T));
T[0]=len;
int tmp[2020];
int flag=0;
for(int i = len;i>0;i--)
{
for(int j = 0;j<=9;j++)
{
T[i]=j;
CF(tmp,T,T);
if(Compare(tmp,A)==1) //如果比较的大于A
{
T[i]=j-1; //退回一个
break;
}
else if(Compare(tmp,A)==0)
{
flag=1;
break;
}
}
if(flag) break;
}
while(T[T[0]]==0&&T[0]>0) T[0]--;
for(int i = 0;i<=T[0];i++)
A[i] = T[i];
}
int main()
{
string s1,s2;
cin>>s1>>s2;
num1[0] = s1.size();
num2[0] = s2.size();
for(int i =1;i<=num1[0];i++)
num1[i] = s1[num1[0]-i]-'0';
for(int i =1;i<=num2[0];i++)
num2[i] = s2[num2[0]-i]-'0';
int endnum[2020];
Sqrt(num1);
Sqrt(num2);
CF(endnum,num1,num2); //乘法
for(int i = endnum[0];i>=1;i--)
printf("%d",endnum[i]);
return 0;
}