矩阵翻硬币
小明先把硬币摆成了一个 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次方)。
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
一开始不会做,自己看的别人的:
1.很容易得出,如果一枚硬币被翻了奇数次,那么它原来的状态肯定是反面朝上,所以,我们要找的就是被翻了奇数次的硬币
2. 根据Q操作定义,我们举个例子,对于(2,3)这个点只有在(1,1)(1,3)(2,1)(2,3)这四个点进行Q操作时才翻转,一共翻转了4次,通过多个例子总结不难看出,(x,y)点再所有点进行完Q操作后翻转的次数为a*b次,其中a为x的约数,b为y的约数。因此若想要这个硬币被翻奇数次,a和b必须都得是奇数,即x和y都有奇数个约数。
先普及一个数论知识:完全平方数有奇数个约数。
那个这个问题就转化成了——输入两个数n,m,设小于等于n的完全平方数的个数是a,小于等于m的完全平方数的个数是b,求a*b。那么怎么求小于等于n和m完全平方数的个数呢?
再普及一个知识:
那么这个题目有转化成了——输入两个数n,m,求[sqrt(n)]*[sqrt(m)]。
这样题目就简单很多了,只要解决两个问题就可以
1.大数的乘法
2.大数的平方根取整
问题1解决方法:
先弄两个代表数值的字符串s1,s2,将s1的每一位与s2相乘,存到一个整形数组里
举个例子s1=“123”,s2=“89”
123*89
规律:将s1[i]s2[j]存入num[i+j+1]中,因为num[0]要存最后进位;存完后,对num数组进行倒着计算,比如27实际上就是12389计算中3*9=27,即把7留下2进上去
最后结果就是10947
问题2解决方法:
假如一个数有偶数位n,那么这个数的方根有n/2位;如果n为奇数,那么方根为(n+1)/2位。
然后,让我们实际看一个例子,我们假设这个数就是1200
1.很明显,它有4位,所以它的方根有2位,然后,我们通过下面的方法来枚举出它的整数根
00*00=0<1200
10*10=100<1200
20*20=400<1200
30*30=900<1200
40*40=1600>1200
所以,这个根的十位就是3,然后,再枚举个位
31*31=961<1200
32*32=1024<1200
33*33=1089<1200
34*34=1156<1200
所以,这个根就是34,因为平方增长的速度还是比较快的,所以速度没有太大问题。为了提高速度,我们可以优化比较函数:
还拿上面的例子来说
30*30=900<1200
4040=1600>1200
这两个式子,一般来说,我们应该先计算33=9,然后在9后面添2个0再与1200比较,但由于数据量很大,添零也会消耗时间
于是我们可以计算需要加的0的数量然后用下面的方法直接比较
1.如果第i个数的平方的位数加上需要添加的零的个数之后位数与原数不相等,那么位数大的数值大
2.如果位数相等就没必要再添零,直接进行字符串比较即可
例如:
30*30=900<1200
40*40=1600>1200
十位是3的时候 3*3=9是1位填上两个零后位数位3位小于1200的4位所以900<1200
十位是4的时候 4*4=16是两位,添上两个零后位数为4位等于1200的四位,所以只需比较字符串16与1200的大小
很明显在字符串中16>1200,所以1600>1200
那么添加零的个数怎么算呢?
假设一个数的平方根取整的位数为n,从前往后算目前计算到了第i位,则需要添加2*(n-1-i)个零
例如:1200 平方根取整有2位,目前算到了第0位(从0开始计数)即3030(我们算的是33),需要加2*(2-1-0)=2个零
原文:https://blog.csdn.net/reidsc/article/details/64924051
#include<iostream>
#include<cstring>
#include<string>
using namespace std;
string Strmul(string s1,string s2) { //大数乘法
int num[500]= {0};
string ans;
for(int i=0; i<s1.size(); i++) {
for(int j=0; j<s2.size(); j++) {
num[i+j+1]+=(s1[i]-'0')*(s2[j]-'0');
}
}
for(int i=s1.size()+s2.size()-1; i>0; i--) {
if(num[i]>9) {
num[i-1]+=num[i]/10;
num[i]%=10;
}
}
for(int i=0; i<s1.size()+s2.size(); i++) {
if(!i&&num[i]||i)
ans.push_back(num[i]+'0');
}
return ans;//将结果存在字符串里
}
bool mycmp(string s1,string s2,int pos) { //比较两字符串大小,pos表示s1后面有几个0
if(s1.length()+pos!=s2.length()) {
return s1.length()+pos>s2.length();
} else {
return s1>s2;
}
}
string SqrtStr(string s) { //大数平方根取整
int len;
string ans;
if(s.length()%2==0)
len=s.length()/2;
else len=s.length()/2+1;
for(int i=0; i<len; i++) {
ans.push_back('0');
for(int j=0; j<=9; j++) {
if(mycmp(Strmul(ans,ans),s,2*(len-i-1)))
break;
ans[i]++;
}
ans[i]--;
}
return ans;
}
int main(){
string s1,s2;
cin>>s1>>s2;
cout<<Strmul(SqrtStr(s1),SqrtStr(s2))<<endl;
return 0;
}