一开始在vjudge上看到这题时,标的来源是CSU 1120,第八届湖南省赛D题“平方根大搜索”。今天交题时CSU突然跪了,后来查了一下看哪家OJ还挂了这道题,竟然发现这题是出自UVA的,而且用的原题。
Description
time limit 5s
In binary, the square root of 2, denoted by sqrt(2), is an infinite number 1.0110101000001001111... Given an integer n and a binary string (i.e. a string consisting of 0 and 1) S, your task is to find the first occurrence of S in the fraction part (i.e. the part after the decimal point) of sqrt(n). In case sqrt(n) is an integer, the fraction part is an infinite sequence of zeros.
Input
The first line contains T (T ≤ 100), the number of test cases. Each of the following lines contains an integer n (2 ≤ n ≤ 1, 000, 000) and a binary string S with at most 20 characters.
Output
For each case, print the position of the first character in the first occurrence of S. The first digit after the dot is at position 0. The answer is guaranteed to be no greater than 100.
Sample Input
2
2 101
1202 110011
Sample Output
2
58
Solution
高精度
复杂度 $O(n^3)$
#include <bits/stdc++.h>
using namespace std;
const int N(300), M(125);
int get_len(int n){
if(!n) return 1;
int res=0;
for(;n;res++,n>>=1);
return res;
}
void trans(int n, int *bit, int &len){
int l=get_len(n);
for(int i=len+l-1; i>=len; i--){
bit[i]=n&1, n>>=1;
}
len+=l;
}
int tmp[N], a[N], b[N], len, _len, LEN;
void sqr(){ //error-prone
memset(tmp, 0, sizeof(tmp));
for(int i=0; i<len; i++) for(int j=0; j<len; j++) tmp[i+j]+=a[i]*a[j];
for(int i=2*len-2; i; i--){
tmp[i-1]+=tmp[i]/2;
tmp[i]%=2;
}
trans(tmp[0], b, LEN=0);
for(int i=1; i<=2*len-2; i++) b[LEN++]=tmp[i];
}
int get_int(){
int res=0, l=LEN-2*_len;
for(int i=l-1, _pow=1; i>=0; res+=b[i]*_pow, i--, _pow<<=1); //error-prone
return res;
}
int solve(){
int n; string s, t; cin>>n>>t;
int m=sqrt(n);
if(m*m==n) return 0;
len=_len=0;
trans(m, a, len);
int tmp=len;
while(_len<M){
a[len++]=1, _len++;
sqr();
if(get_int()>=n) a[len-1]=0;
}
for(int i=tmp; i<len; i++) s+='0'+a[i];
return s.find(t);
}
int main(){
//freopen("in", "r", stdin);
int T;
for(cin>>T; T--; cout<<solve()<<endl);
}
有意写得短一些,但还是有50行,太长了都不想看~
总结
总结一下高精度度乘法的写法,虽说也没什么可总结的。
无论二进制还是十进制,写法都一样,以下代码结合题目按二进制写。
1. 大整数的存法:int[]数组,左边高位(左高右低即书写格式,这样处理有一定优越性(真的有优越性吗?)),0-indexed
2. 将长为n的大整数的a[]和长为m的大整数的b[]相乘
先按如下方式将长为n+m-1中间结果存在数组tmp[]中
memset(tmp, 0, sizeof(tmp));
for(int i=0; i<n; i++)
for(int j=0; j<m; j++)
tmp[i+j]+=a[i]*b[j];
再处理出最终结果存到数组c[]中
for(int k=i+j-2; k; k--)
tmp[k-1]+=tmp[k]/2, tmp[k]%=2;
int get_len(int n){
if(!n) return 1;
int res=0;
for(; n; res++, n>>=1);
return res;
}
len=get_len(tmp[0]);
for(int i=len-1; i>=0; i--)
c[i]=tmp[0]%2, tmp[0]/=2;
for(int k=1; k<i+j-1; k++)
c[len++]=tmp[k];