FFT-hdu题目练习
网上FFT的讲解和板子有很多,所以直接放题目
hdu1402 http://acm.hdu.edu.cn/showproblem.php?pid=1402
1 /* 2 3 problem:大整数乘法 4 5 solution:FFT 6 两个n-1阶多项式的乘积表示为 a0*x^0 + a1*x^1 + ... + a(2n-2)*x^(2n-2) 7 FFT可以O(nlogn)求出系数 a0, a1... a(2n-2) 8 10进制整数x可以表示成 a0*10^0 + a1*10^1 + a2*10^2 + ... 9 因此可以使用FFT求出其结果 10 11 */ 12 #include <bits/stdc++.h> 13 14 using namespace std; 15 16 const int maxn = 550010; 17 18 const double pi = acos(-1.0); 19 20 typedef complex<double> dob; 21 22 int n, m, len1, len2, N, L, R[maxn], c[maxn]; 23 24 dob a[maxn], b[maxn]; 25 26 char s1[maxn], s2[maxn]; 27 28 void fft(dob *A, int f) { 29 for(int i = 0;i < n;i ++) 30 if(i < R[i]) 31 swap(A[i], A[R[i]]); 32 for(int i = 1;i < n;i <<= 1) { 33 dob wn(cos(pi / i), sin(f * pi / i)), x, y; 34 for(int j = 0;j < n;j += (i << 1)) { 35 dob w(1, 0); 36 for(int k = 0;k < i;k ++, w *= wn) { 37 x = A[j + k], y = w * A[i + j + k]; 38 A[j + k] = x + y; 39 A[i + j + k] = x - y; 40 } 41 } 42 } 43 } 44 45 int main() { 46 while(~scanf("%s %s", s1, s2)) { 47 len1 = n = strlen(s1); 48 len2 = m = strlen(s2); 49 m += n, L = 0; 50 for(n = 1;n <= m;n <<= 1) L ++; 51 for(int i = 0;i < len1;i ++) 52 a[i] = s1[len1 - 1 - i] - '0'; 53 for(int i = len1;i < n;i ++) 54 a[i] = 0; 55 for(int i = 0;i < len2;i ++) 56 b[i] = s2[len2 - 1 - i] - '0'; 57 for(int i = len2;i < n;i ++) 58 b[i] = 0; 59 memset(c, 0, sizeof c); 60 for(int i = 0;i < n;i ++) 61 R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1)); 62 fft(a, 1), fft(b, 1); 63 for(int i = 0;i <= n;i ++) 64 a[i] *= b[i]; 65 fft(a, -1), N = 0; 66 for(int i = 0;i <= m;i ++) { 67 c[i] += int(a[i].real() / n + 0.5); 68 if(c[i] > 9) c[i + 1] += c[i] / 10, c[i] %= 10; 69 if(c[i] > 0) N = i; 70 } 71 for(int i = N;~i;i --) 72 printf("%d", c[i]); 73 puts(""); 74 } 75 return 0; 76 }
hdu4609 http://acm.hdu.edu.cn/showproblem.php?pid=4609
1 /* 2 3 problem:给定n个长度为ai的木棍 4 求,任选三个使其能够组成三角形的概率 5 6 solution: 7 ai <= 1e5,先统计c[i]代表长度为i的木棍有多少个 8 对c做卷积,即使用FFT求出 rep(i,0,1e5) rep(j,0,1e5) d[i+j]+=c[i]*c[j] 的d数组 9 d[i]即代表选出两个木棍长度之和为i的方案数 10 去除重复,减去使用了同一木棍两次的,i+j与j+i应为同一方案,所以rep(i,0,2e5) d[i]/=2 11 现在d[i]已经可以表示选出两个不同木棍长度之和为i的不同方案数了 12 13 接下来对木棍长度ai从小到大排序,对d做前缀和sd 14 枚举最长边为第i根木棍,则另外两条边长度之和>ai,ans+=sd[2e5]-sd[ai] 15 这些方案里需要去除一些不满足要求(ai为最长边)的 16 1.另外两条边两条均>ai,ans-=(n-i)*(n-i-1)/2 17 2.另外两条边一条>ai,一条<ai,ans-=(n-i)*(i-1) 18 3.另外两条边一条=ai,另一条随意,ans-=n-1 19 20 */ 21 #include <bits/stdc++.h> 22 23 using namespace std; 24 25 const int maxn = 300010; 26 27 const double pi = acos(-1.0); 28 29 typedef long long ll; 30 31 typedef complex<double> dob; 32 33 int T, N, n, r, L, R[maxn], X[maxn]; 34 35 dob a[maxn]; 36 37 ll ans, b[maxn]; 38 39 void fft(dob *A, int f) { 40 for(int i = 0;i < N;i ++) 41 if(i < R[i]) 42 swap(A[i], A[R[i]]); 43 for(int i = 1;i < N;i <<= 1) { 44 dob wn(cos(pi / i), sin(f * pi / i)), x, y; 45 for(int j = 0;j < N;j += (i << 1)) { 46 dob w(1, 0); 47 for(int k = 0;k < i;k ++, w *= wn) { 48 x = A[j + k], y = w * A[i + j + k]; 49 A[j + k] = x + y; 50 A[i + j + k] = x - y; 51 } 52 } 53 } 54 } 55 56 int main() { 57 for(scanf("%d", &T);T --;) { 58 scanf("%d", &n), ans = r = L = 0; 59 memset(b, 0, sizeof b); 60 for(int i = 1;i <= n;i ++) { 61 scanf("%d", &X[i]); 62 r = max(r, X[i]); 63 b[X[i]] ++; 64 } 65 for(N = 1;N <= (r + 1) * 2;N <<= 1) L ++; 66 for(int i = 0;i < N;i ++) a[i] = b[i]; 67 for(int i = 0;i < N;i ++) 68 R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1)); 69 fft(a, 1); 70 for(int i = 0;i < N;i ++) 71 a[i] *= a[i]; 72 fft(a, -1); 73 for(int i = 0;i < N;i ++) 74 b[i] = ll(a[i].real() / N + 0.5); 75 for(int i = 1;i <= n;i ++) 76 b[X[i] * 2] --; 77 for(int i = 0;i < N;i ++) 78 b[i] >>= 1; 79 for(int i = 1;i < N;i ++) 80 b[i] += b[i - 1]; 81 sort(X + 1, X + n + 1), b[N] = b[N - 1]; 82 for(int i = 1;i <= n;i ++) { 83 ans += b[N] - b[X[i]]; 84 ans -= 1ll * (i - 1) * (n - i); 85 ans -= (n - 1); 86 ans -= 1ll * (n - i) * (n - i - 1) / 2; 87 } 88 printf("%.7f\n", 1.0 * ans / (1ll * n * (n - 1) * (n - 2) / 6)); 89 } 90 return 0; 91 }
hdu5885 http://acm.hdu.edu.cn/showproblem.php?pid=5885
1 /* 2 3 problem: 4 n*m的矩形,每个点有p(i,j) 5 你可以选择一个点(x,y),那么在该点可以得到的价值val为 6 rep(i,1,n) rep(j,1,m) { 7 dis=(i,j)与(x,y)欧几里得距离 8 if(dis<R) val+=p(i,j)/(dis+1) 9 } 10 要求选取一个点,使val最大,输出val 11 12 solution: 13 利用圆的中心对称性,使FFT之后某个点的val都在一个系数上 14 最终构造得到的多项式系数 M=m+R*2 15 A[i*M+j]=p[i][j] B[(i+R)*M+j+R]=1.0/(sqrt(i*i+j*j)+1) 16 val[i][j]=C[(i+R)*M+j+R] 17 18 附:这题内存限制不大,建议A,B变换完成之后,令A*=B即可,舍去C数组防止MLE 19 20 */ 21 #include <bits/stdc++.h> 22 23 using namespace std; 24 25 typedef long long ll; 26 27 namespace FFT { 28 const int maxn = 2100000; 29 //maxn >= (1 << k) >= n + m 30 const double pi = acos(-1.0); 31 32 typedef complex<double> dob; 33 34 int N_, L, R[maxn]; 35 36 dob a[maxn], b[maxn]; 37 38 void fft(dob *A, int f) { 39 for(int i = 0;i < N_;i ++) 40 if(i < R[i]) 41 swap(A[i], A[R[i]]); 42 for(int i = 1;i < N_;i <<= 1) { 43 dob wn(cos(pi / i), sin(f * pi / i)), x, y; 44 for(int j = 0;j < N_;j += (i << 1)) { 45 dob w(1, 0); 46 for(int k = 0;k < i;k ++, w *= wn) { 47 x = A[j + k], y = w * A[i + j + k]; 48 A[j + k] = x + y; 49 A[i + j + k] = x - y; 50 } 51 } 52 } 53 } 54 55 void solve(int n, int m) { 56 n += m; 57 for(N_ = 1;N_ <= n;N_ <<= 1) L ++; 58 for(int i = 0;i < N_;i ++) 59 R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1)); 60 fft(a, 1), fft(b, 1); 61 for(int i = 0;i < N_;i ++) 62 a[i] *= b[i]; 63 fft(a, -1); 64 } 65 66 void clean() { 67 for(int i = 0;i < N_;i ++) 68 a[i] = b[i] = 0; 69 L = 0; 70 } 71 } 72 73 using namespace FFT; 74 75 int n, m, N, M, rr; 76 77 double r; 78 79 int main() { 80 double x, ans; 81 while(~scanf("%d %d %lf", &n, &m, &r)) { 82 rr = r, N = n + rr * 2, M = m + rr * 2, ans = 0; 83 for(int i = 0;i < n;i ++) 84 for(int j = 0;j < m;j ++) 85 scanf("%lf", &a[i * M + j]); 86 for(int i = -rr;i <= rr;i ++) 87 for(int j = -rr;j <= rr;j ++) { 88 x = sqrt(i * i + j * j); 89 if(r > x) b[(i + rr) * M + j + rr] = 1.0 / (x + 1); 90 } 91 solve(N * M, N * M); 92 for(int i = 0;i < n;i ++) 93 for(int j = 0;j < m;j ++) 94 ans = max(ans, a[(i + rr) * M + j + rr].real() / N_); 95 printf("%.3f\n", ans); 96 clean(); 97 } 98 return 0; 99 }
持续更新