矩阵翻硬币(大数乘,开方)

矩阵翻硬币

小明先把硬币摆成了一个 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
这两个式子,一般来说,我们应该先计算3
3=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;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值