矩阵翻硬币
小明先把硬币摆成了一个 n 行 m 列的矩阵。
随后,小明对每一个硬币分别进行一次 Q 操作。
对第x行第y列的硬币进行 Q 操作的定义:将所有第 ix 行,第 jy 列的硬币进行翻转。
其中i和j为任意使操作可行的正整数,行号和列号都是从1开始。
当小明对所有硬币都进行了一次 Q 操作后,他发现了一个奇迹——所有硬币均为正面朝上。
小明想知道最开始有多少枚硬币是反面朝上的。于是,他向他的好朋友小M寻求帮助。
聪明的小M告诉小明,只需要对所有硬币再进行一次Q操作,即可恢复到最开始的状态。然而小明很懒,不愿意照做。于是小明希望你给出他更好的方法。帮他计算出答案。
- :Segmentation fault
#include<iostream>
using namespace std;
int f[10^1000][10^1000];
int dp[10^1000];
int main()
{
int target=0;
int N,M;
cin>>N>>M;
int A;
A=max(N,M);
for(int a=1;a<=A;a++)
{
for(int i=1;i<=a;i++)
{
if(a%i==0)
dp[a]++;
}
}
for(int n=1;n<=N;n++)
for(int m=1;m<=M;m++)
{
f[n][m]=dp[n]*dp[m];
if(f[n][m]%2==1)
target++;
}
cout<<target<<endl;
return 0;
}
有个愚蠢的人想用常规方法解决这个问题,奈何数据量太大,超时又溢出(int = long int = 231-1=2* 1010 ;long long int 263-1=9*1018),甚至还考虑用DP,笑死,根本用不了···
#include <iostream>
#include <algorithm>
using namespace std;
string StrMul(string s1,string s2)//大数乘法
{
string ans;
int num[500]={0},i,j;
for(i=0;i<s1.length();i++)//s计算存到num中
for(j=0;j<s2.length();j++)
num[i+j+1]+=(s1[i]-'0')*(s2[j]-'0');
for(i=s1.length()+s2.length()-1;i>0;i--)//num的处理
if(num[i]>=10)
{
num[i-1]+=num[i]/10;
num[i]%=10;
}
for(int i=0;i<=s1.length()+s2.length()-1;i++)//将num数存到ans字串中,注意进位为0的情况
if(!i&&num[i]||i)
ans.push_back(num[i]+'0');
return ans;
}
bool StrCmp(string s1,string s2,int pos)//比较两字符串大小,pos代表应该在s1后面填几个零
{
if(s1.length()+pos!=s2.length())//如果s1位数不等于s2,
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(StrCmp(StrMul(ans,ans),s,2*(len-1-i)))//需要添加0的个数是2*(len-1-i)解析见上面
break;
ans[i]++;
}
ans[i]--;
}
return ans;
}
int main()
{
string s1,s2;
cin>>s1>>s2;
cout<<StrMul(SqrtStr(s1),SqrtStr(s2))<<endl;
return 0;
}
注意到,完全平方数有奇数个约数。同时,小于等于n的完全平方数的个数为[sqrt(n)]个,即为n的平方根向下取整。
问题转化为 解决大数的乘法与平方根取整 。
大数乘法:运用数组,对各位上的数字对应相乘对应相加。
大数平方根取整:
1.如果第i个数的平方的位数加上需要添加个零之后位数与原数不相等,那么位数大的数值大
2.如果位数相等就没必要再添零,直接进行字符串比较即可