题目:给出两个n位10进制整数x和y,你需要计算x*y。($n leq 60000$)
分析:
两个正整数的相乘可以视为两个多项式的相乘,
例如 $15 imes 16 = 240$,
可写成 $(5+x)*(6+x) = 30 + 11x + x^2$,$x=10$
这样得到多项式 $A(x)$ 和 $B(x)$,并且能用FFT求出 $C(x)=A(x)B(x)$,
怎么得到最终结果,我们要将 $x=10$ 代入吗?
$n$ 这么大,遍历一遍也没有这么大的数据类型能存下,其次,这也不是必要的。
$x=10$ 是 $C(x)$ 已经相当于十进制,我们模拟一下进位就可以了。
// luogu-judger-enable-o2 #include<iostream> #include<cstdio> #include<cmath> using namespace std; const int MAXN = 4 * 60000 + 10; inline int read() { char c = getchar(); int x = 0, f = 1; while (c < '0' || c > '9') {if (c == '-')f = -1; c = getchar();} while (c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();} return x * f; } const double Pi = acos(-1.0); const double Eps = 1e-8; double ccos[MAXN], ssin[MAXN]; struct complex { double x, y; complex (double xx = 0, double yy = 0) {x = xx, y = yy;} } a[MAXN], b[MAXN]; complex operator + (complex a, complex b) { return complex(a.x + b.x , a.y + b.y);} complex operator - (complex a, complex b) { return complex(a.x - b.x , a.y - b.y);} complex operator * (complex a, complex b) { return complex(a.x * b.x - a.y * b.y , a.x * b.y + a.y * b.x);} //不懂的看复数的运算那部分 void fast_fast_tle(int limit, complex *a, int type) { if (limit == 1) return ; //只有一个常数项 complex a1[limit >> 1], a2[limit >> 1]; for (int i = 0; i < limit; i += 2) //根据下标的奇偶性分类 a1[i >> 1] = a[i], a2[i >> 1] = a[i + 1]; fast_fast_tle(limit >> 1, a1, type); fast_fast_tle(limit >> 1, a2, type); complex Wn = complex(ccos[limit] , type * ssin[limit]), w = complex(1, 0); //complex Wn = complex(cos(2.0 * Pi / limit) , type * sin(2.0 * Pi / limit)), w = complex(1, 0); //Wn为单位根,w表示幂 for (int i = 0; i < (limit >> 1); i++, w = w * Wn) //这里的w相当于公式中的k { complex tmp = w * a2[i]; a[i] = a1[i] + tmp; a[i + (limit >> 1)] = a1[i] - tmp; //利用单位根的性质,O(1)得到另一部分 } } char s[MAXN]; int res[MAXN]; int main() { int N = read(); scanf("%s", s); for (int i = 0; i < N; i++) a[i].x = s[N-1-i]-'0'; scanf("%s", s); for (int i = 0; i < N; i++) b[i].x = s[N-1-i]-'0'; //for(int i = 0;i < N;i++) printf("%f ", a[i]); int limit = 1; while (limit <= 2*N) limit <<= 1; for(int i = 1;i <= limit;i++) { ccos[i] = cos(2.0 * Pi / i); ssin[i] = sin(2.0 * Pi / i); } fast_fast_tle(limit, a, 1); fast_fast_tle(limit, b, 1); //后面的1表示要进行的变换是什么类型 //1表示从系数变为点值 //-1表示从点值变为系数 //至于为什么这样是对的,可以参考一下c向量的推导过程, for (int i = 0; i <= limit; i++) a[i] = a[i] * b[i]; fast_fast_tle(limit, a, -1); for(int i = 0;i <= 2*N;i++) res[i] = int(a[i].x/limit+0.5); int tmp = 0; //进位 for(int i = 0;i <= 2*N;i++) { res[i] += tmp; tmp = res[i] / 10; res[i] = res[i] % 10; } bool flag = false; for (int i = 2*N; i >= 0; i--) { if(res[i]) flag = true; //注意处理前导0,题干有说 if(flag) printf("%d", res[i]); //按照我们推倒的公式,这里还要除以n } return 0; }