蓝桥杯T126(xjb&大数开方)

时间:2023-03-09 19:03:23
蓝桥杯T126(xjb&大数开方)

题目链接:http://lx.lanqiao.cn/problem.page?gpid=T126

题意:中文题诶~

思路:显然被翻转了奇数次的硬币为反面朝上,但是本题的数据量很大,所以O(n^2)枚举每个点肯定是不行的...

可以反过来想一下,对于一个坐标 (i, j),显然其只被坐标 (x, y) 影响当且仅当 x 是 i 的因子或者 y 是 j 的因子;

用 f(x) 表示 x 的因子数目,那么坐标 (i, j) 反面朝上的充要条件为 f(i)*f(j) 为奇数,即 f(i) 为奇数并且 f(j) 为奇数;

显然当且仅当 i 为完全平方数的时候 f(i) 为奇数;那么对于 n*m 的矩阵,反面朝上的硬币数目为:

小于等于n的完全平方数的数目 * 小于等于m的完全平方数的数目,即 sqrt(n)*sqrt(m);

注意:这里的n, m范围为1e1000,需要写个大数开方;

代码:

 #include<iostream>
#include<string>
#include<string.h>
using namespace std; const int MAXN = 1e3+;
string s1, s2; //n,m;
int len1, len2; //记录开根号后大数的位数; int sqrta[MAXN], sqrtb[MAXN];
int a[MAXN], temp[MAXN], ans[MAXN]; int compare(int a[], int b[], int len1, int len2){
if(len1 > len2) return ;
else if(len1 < len2) return -;
for(int i=len1-; i>=; i--){
if(a[i] > b[i]) return ;
else if(a[i] < b[i]) return -;
}
return ;
} //计算A[]*B[],返回答案长度
int multi(int *ans, int A[], int B[], int len1, int len2){
for(int i=; i<=; i++) ans[i]=;//传址数组不能用memset初始化
for(int i=; i<len1; i++){
for(int j=; j<len2; j++){
ans[i+j] += A[i]*B[j];
}
}
for(int i=; i<len1+len2; i++){
ans[i+] += ans[i]/;
ans[i] %= ;
}
int i;
for(i=len1+len2; i>=; i--){
if(ans[i]) break;
}
return i+;
} //对s开方,结果倒序存储于A数组,返回答案长度
int get_sqrt(int *A, string s){
memset(A, , sizeof(A));
memset(a, , sizeof(a));
int len1 = s.size();
int len2 = len1>>;
if(len1 & ) len2 += ;
for(int i=,j=s.size()-; i<s.size(); i++,j--){//翻转
a[j]=s[i]-'';
}
for(int i=len2-; i>=; i--){//从最高位开始
int flag;
int lenMul=;
memset(temp, , sizeof(temp));
while((flag=compare(temp, a, lenMul, len1))==-){
A[i]++;
lenMul=multi(temp, A, A, len2, len2);
}
if(flag==) break;
else if(flag==) A[i]--;
}
return len2;
} int main(void){
memset(sqrta, , sizeof(sqrta));
memset(sqrtb, , sizeof(sqrtb));
cin >> s1 >> s2;
len1 = get_sqrt(sqrta, s1);
len2 = get_sqrt(sqrtb, s2);
int len = multi(ans, sqrta, sqrtb, len1, len2);
for(int i=len-; i>=; i--){
cout << ans[i];
}
cout << endl;
return ;
}