今天再看蓝桥杯的时候,看见一道有趣的贪心题。
问题描述
小明先把硬币摆成了一个 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次方)。
可以知道,假设是一个(1,m)的矩阵,那么某个硬币被翻的次数是由它所在的位置决定的,假设一个硬币位置为(1,6)那么,在翻(1,1),(1,2),(1,3)时都会被翻一次,加上其自身,翻到的次数就是四次;最后状态不变。如果一个硬币的位置是(1,5),那么翻(1,1),(1,5)的时候被翻转;结果状态不变。如果是(1,9),那么(1,1),(1,3),(1,9)时翻转,状态改变。相同,如果是(1,4),状态也会改变,其原因就是该数的两个约数相等,所以减少了一次翻转的机会。所以对于这个行数为1的矩阵,状态被改变的数目也就是在m以内所有可开方数的数目和,我们只要知道m以内有多少个数可以写成m=k^2的方式即可。
我们假设m=30,那么最大的看开方数是5(5^2=25),所以,就必定存在4,3,2,1四个可开方数,结合上面的推论,有5个硬币的状态被改变,最初共有5个反面的硬币。
在我们计算的时候,只需要求出一个数的最大可开方数取整,即是可开方数的个数。
将范围扩大到(n,m),这时要考虑的是行数增加的影响,若位置是(2,9),该硬币不但会在(2,1),(2,3),(2,9)时被翻转三次,还会在(1,1),(1,3),(1,9)时被翻转,故在行,列,位置都是不可开方数的时候才会出现状态改变,设n,m的可开方数目分别为i,j,那么该矩阵的总反面硬币个数为i与j的积。
因此我们的计算方法为:
1,寻找两个数的最大可开方数
2,将最大可开方数相乘
具体实现涉及到了大数的计算,字符串的应用问题,在这里就不多说了。具体代码演示:
#include <iostream>
#include <string>
#include <string.h>
#include <sstream>
using namespace std;
string multiply(string str1,string str2)//字符串相乘函数
{
string str=""; //最终结果
int len1=str1.size(),len2=str2.size(),i,j;//计算两个字符串的函数
int result[1000]={0}; //开辟数组存乘积并初始化
for(i=len1-1;i>=0;i--)
for(j=len2-1;j>=0;j--)
{
result[i+j+1]+=(str1[i]-'0')*(str2[j]-'0');
}
for(i=len1+len2-1;i>=1;i--)//让数组的每一位都是正确的数
{
result[i-1]=result[i-1]+result[i]/10;
result[i]=result[i]%10;
}
for(i=0;result[i]==0;i++) //前导零不要
;
for(j=i;j<len1+len2;j++)//转成字符串形式
str+=result[j]+'0';
return str;
}
int compare(string str1,string str,int pos)//字符串比较函数
{
int len1=str1.size();
int len2=str.size();
if(len2>len1+pos)return 0;
if(len2<len1+pos)return 1;
for(int i=0;i<len2;i++)
{
if(str1[i]-'0'>str[i]-'0')return 1;
if(str1[i]-'0'<str[i]-'0')return 0;
}
}
string strsqrt(string str)//对字符串开方取整函数
{
int len=str.size();
string str1="",str2="";
for(int i=0;i<(len+1)/2;i++)//若len为偶数,len/2=(len+1)/2;若len为奇数,len/2+1=(len+1)/2
for(int j=0;j<=9;j++) //因为每一位的数值都是0~9
{
str1=str2;
str1+=j+'0';
if(compare(multiply(str1,str1),str,2*((len+1)/2-1-i))==1)//由于str1后少了(len+1)/2-i-1个0,所以平方以后少了2*((len+1)/2-i-1)个
{
str2+=j-1+'0';
break;
}
if(j==9)
str2+='9';
}
return str2;
}
int main()
{
string n,m;
cin>>n>>m;
cout<<multiply(strsqrt(n),strsqrt(m))<<endl;
return 0;
}