思路:要推出一开始有多少枚硬币反面朝上,我们从最终全部正面朝上开始出发,对每个位置进行Q操作得出初态。然后现在来考虑,对于任意一枚正面朝上的硬币,经过奇数次的反转,肯定变为反面朝上。所以只要计算经过奇数次反转的硬币就可以得出答案了。接下来要做就是对于某一个位置的硬币(x,y)它的反转次数的奇偶如何确定呢。手动模拟可以得出结论:能影响到(x,y)反转的是(i,j)(i,j分别是x和y的约数)。组合起来一共有i*j次反转。而反转为奇数,即i*j为奇数,当且仅当i j都为奇数。接下来问题就变成在m*n的矩阵中分别找出行、列编号有奇数个约数的个数,相乘即为所求。(一个数的约数个数为奇数,这个数是一个完全平方数,且 1-n内的完全平方数个数为sqrt(n)),所以最后结果为sqrt(n)*sqrt(m) 不过由于数据巨大,这里要用到大数相乘和开方。
不过比赛的时候是不可以带模板的,大数相乘就GG了,直接用sqrt 和*能得40分。嗯,比赛时也不错了。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
long long n,m,cnt;
string s1,s2;
string strMul(string a,string b)
{
string result="";
int len1=a.length();
int len2=b.length();
int i,j;
int num[500]={0};
for(i=0;i<len1;i++)
for(j=0;j<len2;j++)
{
num[len1-1+len2-1-i-j]=num[len1-1+len2-1-i-j]+(a[i]-'0')*(b[j]-'0');
}
//for(i=0;i<5;i++)
//cout<<num[i]<<' ';
//cout<<endl;
for(i=0;i<len1+len2;i++)
{
num[i+1]=num[i+1]+num[i]/10;
num[i]=num[i]%10;
}
//for(i=0;i<5;i++)
//cout<<num[i]<<' ';
for(i=len1+len2-1;i>=0;i--)
{
if(num[i]!=0)
break;
}
for(;i>=0;i--)
{
result=result+(char)(num[i]+'0');
}
return result;
}
int strCmp(string a,string b,int pos)
{
int i;
//cout<<a<<endl;
//cout<<a.length()<<' '<<b.length()<<endl;
if(a.length()+pos>b.length())
return 1;
if(a.length()+pos<b.length())
return 0;
if(a.length()+pos==b.length())
{
for(i=0;i<a.length();i++)
{
if(a[i]<b[i])
return 0;
if(a[i]==b[i])
continue;
if(a[i]>b[i])
return 1;
}
}
}
string strSqrt(string a)
{
string result="";
int i;
int len=a.length();
if(len%2==0)
len=len/2;
else
len=len/2+1;
for(i=0;i<len;i++)
{
result=result+'0';
while(strCmp(strMul(result,result),a,2*(len-1-i))!=1)
{
if(result[i]==':')
break;
result[i]++;
}
result[i]--;
}
return result;
}
int main(){
while(cin>>s1>>s2){
cout<<strMul(strSqrt(s1),strSqrt(s2))<<endl;
}
return 0;
}