Fast Fourier Transform

写在前面的..

感觉自己是应该学点新东西了.. 所以就挖个大坑,去学FFT了..

 


FFT是个啥?

坑已补上..

推荐去看黑书《算法导论》,讲的很详细

 


例题选讲


1.UOJ #34. 多项式乘法

这是FFT最裸的题目了 FFT就是拿来求这个东西的

没啥好讲的,把板子贴一下吧..

 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <algorithm>
 5 #include <cmath>
 6 using namespace std;
 7 const int Maxn = 400010;
 8 struct cp {
 9     double r, i;
10     cp ( double r=0, double i=0 ) : r(r), i(i) {}
11 }A[Maxn], B[Maxn]; int an, bn, n;
12 cp operator + ( cp a, cp b ){ return cp ( a.r+b.r, a.i+b.i ); }
13 cp operator - ( cp a, cp b ){ return cp ( a.r-b.r, a.i-b.i ); }
14 cp operator * ( cp a, cp b ){ return cp ( a.r*b.r-a.i*b.i, a.r*b.i+a.i*b.r ); }
15 double pi = acos(-1);
16 void fft ( cp *a, int op ){
17     int i, j, k;
18     j = 0;
19     for ( i = 0; i < n; i ++ ){
20         if ( i < j ) swap ( a[i], a[j] );
21         k = n>>1;
22         while ( j & k ){ j -= k; k >>= 1; }
23         j += k;
24     }
25     for ( i = 2; i <= n; i <<= 1 ){
26         cp wn = cp ( cos (2.0*pi/i), op * sin (2.0*pi/i) );
27         for ( j = 0; j < n; j += i ){
28             cp w = cp ( 1, 0 );
29             for ( k = j; k < j+i/2; k ++ ){
30                 cp x = a[k], y = w*a[k+i/2];
31                 a[k] = x+y; a[k+i/2] = x-y;
32                 w = w*wn;
33             }
34         }
35     }
36     if ( op == -1 ) for ( i = 0; i < n; i ++ ) a[i].r /= n;
37 }
38 int main (){
39     int i, j, k;
40     scanf ( "%d%d", &an, &bn );
41     n = 1; while ( n < an+bn+1 ) n <<= 1;
42     for ( i = 0; i <= an; i ++ ) scanf ( "%lf", &A[i].r );
43     for ( i = 0; i <= bn; i ++ ) scanf ( "%lf", &B[i].r );
44     fft ( A, 1 ); fft ( B, 1 );
45     for ( i = 0; i < n; i ++ ) A[i] = A[i]*B[i];
46     fft ( A, -1 );
47     for ( i = 0; i <= an+bn; i ++ ) printf ( "%d ", (int)(A[i].r+0.5) );
48     printf ( "\n" );
49     return 0;
50 }
View Code

 


2.bzoj 3527: [Zjoi2014]力

这题在bzoj上没有题面,百度一下就行了,给个链接吧

化简一下公式:$$E_i=\sum\limits_{j<i}\dfrac{q_j}{(i-j)^2}-\sum\limits_{j>i}\dfrac{q_j}{(i-j)^2}$$

然后你设$g_i=\dfrac{1}{i^2}$,只考虑前面一部分的公式就可化成:$$E_i=\sum\limits_{j<i}q_j\times g_{i-j}$$

看是不是很眼熟,这就是很典型的FFT的模型

然后后面的就把整个数组反过来搞就是同样的道理..

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <algorithm>
 5 #include <cmath>
 6 using namespace std;
 7 const int Maxn = 100010;
 8 struct cp {
 9     double r, i;
10     cp ( double r=0.0, double i=0.0 ) : r(r), i(i) {}
11 }g[Maxn*4], h[Maxn*4];  int n, nn;
12 cp operator + ( cp a, cp b ){ return cp ( a.r+b.r, a.i+b.i ); }
13 cp operator - ( cp a, cp b ){ return cp ( a.r-b.r, a.i-b.i ); }
14 cp operator * ( cp a, cp b ){ return cp ( a.r*b.r-a.i*b.i, a.r*b.i+a.i*b.r ); }
15 double pi = acos (-1);
16 void fft ( cp *a, int op ){
17     int i, j, k;
18     for ( i = j = 0; i < n; i ++ ){
19         if ( i < j ) swap ( a[i], a[j] );
20         k = n >> 1;
21         while ( j & k ) j -= k, k >>= 1;
22         j += k;
23     }
24     for ( i = 2; i <= n; i <<= 1 ){
25         cp wn = cp ( cos (2.0*pi/i), op * sin (2.0*pi/i) );
26         for ( j = 0; j < n; j += i ){
27             cp w = cp ( 1, 0 );
28             for ( k = j; k < j+i/2; k ++ ){
29                 cp x = a[k], y = w*a[k+i/2];
30                 a[k] = x+y; a[k+i/2] = x-y;
31                 w = w*wn;
32             }
33         }
34     }
35     if ( op == -1 ) for ( i = 0; i < n; i ++ ) a[i].r /= n;
36 }
37 double q[Maxn], ans[Maxn];
38 int main (){
39     int i, j, k;
40     scanf ( "%d", &nn );
41     for ( i = 1; i <= nn; i ++ ) scanf ( "%lf", &q[i] );
42     n = 1; while ( n < 2*nn+1 ) n <<= 1;
43     for ( i = 1; i <= nn; i ++ ){
44         g[i].r = q[i];
45         h[i].r = (double)1/(double(i)*i);
46     }
47     fft ( g, 1 ); fft ( h, 1 );
48     for ( i = 0; i < n; i ++ ) g[i] = g[i]*h[i];
49     fft ( g, -1 );
50     for ( i = 1; i <= nn; i ++ ) ans[i] += g[i].r;
51  
52     for ( i = 0; i <= n; i ++ ) g[i].r = g[i].i = h[i].r = h[i].i = 0;
53     for ( i = 1; i <= nn; i ++ ){
54         g[i].r = q[nn-i+1];
55         h[i].r = (double)1/(double(i)*i);
56     }
57     fft ( g, 1 ); fft ( h, 1 );
58     for ( i = 0; i < n; i ++ ) g[i] = g[i]*h[i];
59     fft ( g, -1 );
60     for ( i = 1; i <= nn; i ++ ) ans[i] -= g[nn-i+1].r;
61     for ( i = 1; i <= nn; i ++ ) printf ( "%lf\n", ans[i] );
62     return 0;
63 }
View Code

3.bzoj 3160: 万径人踪灭

题意很长,但有用的不过那两句划线的..

仔细想想,如果$s_i==s_j$,那么就可以对$\dfrac{i+j}{2}$贡献答案了对吧

再想想,如果我已经知道了这$2n-1$(包括了中间的插缝)位置有$x$对数贡献了答案,在不考虑连续一段、取空集的情况下是可以有$2^x$种答案的

那么就在上面的基础上减去连续一段和空集的情况

连续一段的可以用manacher判断出,空集直接减去$1$就好了

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <algorithm>
 5 #include <cmath>
 6 using namespace std;
 7 const int Maxn = 100010;
 8 const int Mod = 1e9+7;
 9 struct cp {
10     double r, i;
11     cp ( double r=0.0, double i=0.0 ) : r(r), i(i) {}
12 }A[Maxn*4];
13 cp operator + ( cp a, cp b ){ return cp ( a.r+b.r, a.i+b.i ); }
14 cp operator - ( cp a, cp b ){ return cp ( a.r-b.r, a.i-b.i ); }
15 cp operator * ( cp a, cp b ){ return cp ( a.r*b.r-a.i*b.i, a.r*b.i+a.i*b.r ); }
16 int n, nn;
17 char s[Maxn*2]; int ans[Maxn*2];
18 double pi = acos (-1);
19 int _min ( int x, int y ){ return x < y ? x : y; }
20 void fft ( cp *a, int op ){
21     int i, j, k;
22     for ( i = j = 0; i < n; i ++ ){
23         if ( i < j ) swap ( a[i], a[j] );
24         k = n >> 1;
25         while ( j & k ) j -= k, k >>= 1;
26         j += k;
27     }
28     for ( i = 2; i <= n; i <<= 1 ){
29         cp wn = cp ( cos (2.0*pi/i), op*sin(2.0*pi/i) );
30         for ( j = 0; j < n; j += i ){
31             cp w = cp ( 1, 0 );
32             for ( k = j; k < j+i/2; k ++ ){
33                 cp x = a[k], y = w*a[k+i/2];
34                 a[k] = x+y; a[k+i/2] = x-y;
35                 w = w*wn;
36             }
37         }
38     }
39     if ( op == -1 ) for ( i = 0; i < n; i ++ ) a[i].r /= n;
40 }
41 int d[Maxn];
42 int rad[Maxn*2];
43 int main (){
44     int i, j, k;
45     scanf ( "%s", s );
46     nn = strlen (s);
47     d[0] = 1;
48     for ( i = 1; i <= nn; i ++ ) d[i] = ( d[i-1] * 2 ) % Mod;
49     n = 1; while ( n < 2*nn+1 ) n <<= 1;
50     for ( i = 0; i < nn; i ++ ){
51         if ( s[i] == 'a' ) A[i].r = 1;
52     }
53     fft ( A, 1 );
54     for ( i = 0; i < n; i ++ ) A[i] = A[i]*A[i];
55     fft ( A, -1 );
56     for ( i = 0; i <= 2*(nn-1); i ++ ) ans[i] += ((int)(A[i].r+0.5)+1)/2;
57  
58     for ( i = 0; i < n; i ++ ) A[i].r = A[i].i = 0;
59     for ( i = 0; i < nn; i ++ ){
60         if ( s[i] == 'b' ) A[i].r = 1;
61     }
62     fft ( A, 1 );
63     for ( i = 0; i < n; i ++ ) A[i] = A[i]*A[i];
64     fft ( A, -1 );
65     for ( i = 0; i <= 2*(nn-1); i ++ ) ans[i] += ((int)(A[i].r+0.5)+1)/2;
66     int ret = 0;
67     for ( i = nn-1; i >= 0; i -- ){
68         s[i*2+2] = s[i];
69         s[i*2+3] = '#';
70     }
71     s[0] = '$'; s[1] = '#'; s[2*nn+2] = '?';
72     rad[0] = 0;
73     k = 0; int mx = 0;
74     for ( i = 1; i <= 2*nn; i ++ ){
75         if ( mx > i ){ rad[i] = _min ( rad[2*k-i], mx-i ); }
76         else rad[i] = 1;
77         while ( s[i-rad[i]] == s[i+rad[i]] && i-rad[i] > 0 ) rad[i] ++;
78         if ( i+rad[i]-1 > mx ){
79             mx = i+rad[i]-1;
80             k = i;
81         }
82     }
83     for ( i = 0; i <= 2*(nn-1); i ++ ){
84         ret = (ret+d[ans[i]]-rad[i+2]/2-1)%Mod;
85     }
86     printf ( "%d\n", ret );
87     return 0;
88 }
View Code

4.bzoj 3509: [CodeChef] COUNTARI

来自la1la1la的题解:

1)先看一个比较sb的做法:枚举$j$,算出前面$i$的数的总数$sum[a[i]]$,然后找后面$k$,统计$sum[2*a[j]-a[k]]$

时间复杂度为$O(n^2)$

2)再看一个更sb的做法:枚举$j$,搞搞前面的和后面的,FFT计算,累加和为$2*a[j]$的方案

时间复杂度为$O(n^2logn)$

好,那么这题的正解就是把这两个sb做法合在一起

用一种分块的方法,假设我们把每一块分成每块大小为$S$的

枚举块内的$j$,用第一种方法暴力搞出块内的$i$、$k$的所有情况,时间复杂度为$O(nS)$

对于块外的使用第二种方法,即FFT来做,再枚举块内$j$累计答案,时间复杂度为$O(\frac{n}{S}mlogm)$,其中$m$是最大的数

总时间是$O(nS+\frac{n}{S}mlogm)$,这个时候锅就该甩给你们的高中老师了——均值不等式:$$a+b\geq 2\sqrt{ab}$$

那么这个时间就是大于等于$2n\sqrt{mlogm}$,什么时候能取到等于呢,继续甩锅

当$S=\sqrt{mlogm}$就是了,你把$m=30000$带进去估算一下(记住要带点常数进去..)

大概就是$S=1000$就差不多了 反正我就是这么打的..

 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <algorithm>
 5 #include <cmath>
 6 #define LL long long
 7 using namespace std;
 8 const int Maxn = 100010;
 9 const int Maxm = 33000;
10 struct cp {
11     double r, i;
12     cp ( double r=0.0, double i=0.0 ) : r(r), i(i) {}
13 }A[Maxm*4], B[Maxm*4]; int An;
14 cp operator + ( cp a, cp b ){ return cp ( a.r+b.r, a.i+b.i ); }
15 cp operator - ( cp a, cp b ){ return cp ( a.r-b.r, a.i-b.i ); }
16 cp operator * ( cp a, cp b ){ return cp ( a.r*b.r-a.i*b.i, a.r*b.i+a.i*b.r ); }
17 int a[Maxn], n, S;
18 int _min ( int x, int y ){ return x < y ? x : y; }
19 int _max ( int x, int y ){ return x > y ? x : y; }
20 int ss[Maxm*2], ls[Maxm*2], rs[Maxm*2];
21 double pi = acos(-1);
22 void fft ( cp *a, int op ){
23     int i, j, k;
24     for ( i = j = 0; i < An; i ++ ){
25         if ( i < j ) swap ( a[i], a[j] );
26         k = An >> 1;
27         while ( j & k ) j -= k, k >>= 1;
28         j += k;
29     }
30     for ( i = 2; i <= An; i <<= 1 ){
31         cp wn = cp ( cos (2.0*pi/i), op * sin (2.0*pi/i) );
32         for ( j = 0; j < An; j += i ){
33             cp w = cp ( 1, 0 );
34             for ( k = j; k < j+i/2; k ++ ){
35                 cp x = a[k], y = w*a[k+i/2];
36                 a[k] = x+y; a[k+i/2] = x-y;
37                 w = w*wn;
38             }
39         }
40     }
41     if ( op == -1 ) for ( i = 0; i < An; i ++ ) a[i].r /= An;
42 }
43 int main (){
44     int i, j, k;
45     scanf ( "%d", &n );
46     S = 1000;
47     int mx = 0;
48     for ( i = 1; i <= n; i ++ ){
49         scanf ( "%d", &a[i] );
50         rs[a[i]] ++;
51         mx = _max ( mx, a[i] );
52     }
53     An = 1; while ( An < 2*mx+1 ) An <<= 1;
54     LL ans = 0;
55     for ( i = 1; i <= n; i += S ){
56         int p = _min ( i+S-1, n );
57         for ( j = i; j <= p; j ++ ) rs[a[j]] --;
58         for ( j = i; j <= p; j ++ ){
59             for ( k = j+1; k <= p; k ++ ){
60                 if ( 2*a[j]-a[k] > 0 ) ans += (LL)ss[2*a[j]-a[k]]+ls[2*a[j]-a[k]];
61             }
62             ss[a[j]] ++;
63             for ( k = i; k <= j-1; k ++ ){
64                 if ( 2*a[j]-a[k] > 0 ) ans += (LL)rs[2*a[j]-a[k]];
65             }
66         }
67         for ( j = 0; j < An; j ++ ){
68             A[j].r = ls[j]; A[j].i = 0;
69             B[j].r = rs[j]; B[j].i = 0;
70         }
71         fft ( A, 1 ); fft ( B, 1 );
72         for ( j = 0; j < An; j ++ ) A[j] = A[j]*B[j];
73         fft ( A, -1 );
74         for ( j = i; j <= p; j ++ ){
75             ans += (LL)(A[2*a[j]].r+0.5);
76         }
77         for ( j = i; j <= p; j ++ ) ss[a[j]] --, ls[a[j]] ++;
78     }
79     printf ( "%lld\n", ans );
80     return 0;
81 }
82 
View Code

 


5.hdu 5730 Shell Necklace

先把原题公式化简一下就是:$$dp_i=\sum\limits_{j=1}^{i-1}a_{i-j}\times dp_j$$

那么的话就是cdq分治搞一下再套一个FFT计算更新答案了..

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <algorithm>
 5 #include <cmath>
 6 using namespace std;
 7 const int Maxn = 100010;
 8 const int Mod = 313;
 9 struct cp {
10     double r, i;
11     cp ( double r=0.0, double i=0.0 ) : r(r), i(i) {}
12 }A[Maxn*4], B[Maxn*4]; int An;
13 cp operator + ( cp a, cp b ){ return cp ( a.r+b.r, a.i+b.i ); }
14 cp operator - ( cp a, cp b ){ return cp ( a.r-b.r, a.i-b.i ); }
15 cp operator * ( cp a, cp b ){ return cp ( a.r*b.r-a.i*b.i, a.r*b.i+a.i*b.r ); }
16 int a[Maxn], n, f[Maxn];
17 double pi = acos (-1);
18 void fft ( cp *a, int op ){
19     int i, j, k;
20     for ( i = j = 0; i < An; i ++ ){
21         if ( i < j ) swap ( a[i], a[j] );
22         k = An >> 1;
23         while ( j & k ) j -= k, k >>= 1;
24         j += k;
25     }
26     for ( i = 2; i <= An; i <<= 1 ){
27         cp wn = cp ( cos (2*pi/i), op * sin (2*pi/i) );
28         for ( j = 0; j < An; j += i ){
29             cp w = cp ( 1, 0 );
30             for ( k = j; k < j+i/2; k ++ ){
31                 cp x = a[k], y = w*a[k+i/2];
32                 a[k] = x+y; a[k+i/2] = x-y;
33                 w = w*wn;
34             }
35         }
36     }
37     if ( op == -1 ) for ( i = 0; i < An; i ++ ) a[i].r /= An;
38 }
39 void cdq ( int l, int r ){
40     if ( l == r ) return;
41     int mid = ( l + r ) >> 1;
42     cdq ( l, mid );
43     int i, j, k;
44     int len = r-l;
45     An = 1; while ( An < 2*len+1 ) An <<= 1;
46     for ( i = l; i <= mid; i ++ ){
47         A[i-l].r = f[i]; A[i-l].i = 0;
48         B[i-l].r = a[i-l]; B[i-l].i = 0;
49     }
50     for ( i = mid+1; i <= r; i ++ ){
51         A[i-l].r = 0; A[i-l].i = 0;
52         B[i-l].r = a[i-l]; B[i-l].i = 0;
53     }
54     for ( i = len+1; i < An; i ++ ) A[i].r = A[i].i = B[i].r = B[i].i = 0;
55     fft ( A, 1 ); fft ( B, 1 );
56     for ( i = 0; i < An; i ++ ) A[i] = A[i]*B[i];
57     fft ( A, -1 );
58     for ( i = mid+1; i <= r; i ++ ){
59         int ret = (int)(A[i-l].r+0.5);
60         ret %= Mod;
61         f[i] = ( f[i] + ret ) % Mod;
62     }
63     cdq ( mid+1, r );
64 }
65 int main (){
66     int i, j, k;
67     while ( scanf ( "%d", &n ) != EOF ){
68         if ( n == 0 ) break;
69         for ( i = 1; i <= n; i ++ ){
70             scanf ( "%d", &a[i] ); a[i] %= Mod;
71             f[i] = 0;
72         }
73         f[0] = 1;
74         cdq ( 0, n );
75         printf ( "%d\n", f[n] );
76     }
77     return 0;
78 }
View Code

6.hdu 5307 He is Flying

题意:有$n$段路,每段路长度为$s_i$,你从节点$i$到节点$j$,可以获得一个开心值$j−i+1$,然后问你,主人公走过了所有总长度为$s$的段,问你有多少开心值。

%%%la1la1la

累计一个前缀和$sum$,那么题意就变成走$sum_j-sum_i$的路程获得$j-i$的开心值

那么就把$j$和$-i$分开来算,卷积一下就好了..

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <algorithm>
 5 #include <cmath>
 6 #define LL long long
 7 #define LD long double
 8 using namespace std;
 9 const LL Maxn = 100010;
10 const LL Maxs = 50010;
11 struct cp {
12     LD r, i;
13     cp ( LD r=0.0, LD i=0.0 ) : r(r), i(i) {}
14 }A[Maxs*4], B[Maxs*4]; LL An;
15 cp operator + ( cp a, cp b ){ return cp ( a.r+b.r, a.i+b.i ); }
16 cp operator - ( cp a, cp b ){ return cp ( a.r-b.r, a.i-b.i ); }
17 cp operator * ( cp a, cp b ){ return cp ( a.r*b.r-a.i*b.i, a.r*b.i+a.i*b.r ); }
18 LL sum[Maxn], n, ans[Maxs];
19 const LD pi = acos (-1);
20 void fft ( cp *a, LL op ){
21     LL i, j, k;
22     for ( i = j = 0; i < An; i ++ ){
23         if ( i < j ) swap ( a[i], a[j] );
24         k = An >> 1;
25         while ( j & k ) j -= k, k >>= 1;
26         j += k;
27     }
28     for ( i = 2; i <= An; i <<= 1 ){
29         for ( j = 0; j < An; j += i ){
30             for ( k = j; k < j+i/2; k ++ ){
31                 cp x = a[k], y = cp ( cos (2*pi*(k-j)/i), op * sin (2*pi*(k-j)/i) ) * a[k+i/2];
32                 a[k] = x+y; a[k+i/2] = x-y;
33             }
34         }
35     }
36     if ( op == -1 ) for ( i = 0; i < An; i ++ ) a[i].r /= An;
37 }
38 LL ss[Maxn];
39 int main (){
40     LL i, j, k, T;
41     for ( i = 1; i <= 100000; i ++ ) ss[i] = ss[i-1]+i*(i+1)/2;
42     scanf ( "%I64d", &T );
43     while ( T -- ){
44         scanf ( "%I64d", &n );
45         LL lj = 0, ret = 0;
46         for ( i = 1; i <= n; i ++ ){
47             scanf ( "%I64d", &sum[i] );
48             if ( sum[i] == 0 ) lj ++;
49             else {
50                 ret += ss[lj];
51                 lj = 0;
52             }
53             sum[i] += sum[i-1];
54         }
55         ret += ss[lj];
56         printf ( "%I64d\n", ret );
57         for ( i = 0; i <= sum[n]; i ++ ) ans[i] = 0;
58         An = 1; while ( An < sum[n]*2 ) An <<= 1;
59         for ( i = 0; i < An; i ++ ) A[i].r = A[i].i = B[i].r = B[i].i = 0;
60         for ( i = 0; i <= n; i ++ ){
61             A[sum[n]-sum[i]].r += i;
62             B[sum[i]].r += 1;
63         }
64         fft ( A, 1 ); fft ( B, 1 );
65         for ( i = 0; i < An; i ++ ) A[i] = A[i]*B[i];
66         fft ( A, -1 );
67         for ( i = 0; i <= sum[n]; i ++ ) ans[i] = -(LL)(A[sum[n]+i].r+0.5);
68         for ( i = 0; i < An; i ++ ) A[i].r = A[i].i = B[i].r = B[i].i = 0;
69         for ( i = 0; i <= n; i ++ ){
70             A[sum[n]-sum[i]].r += 1;
71             B[sum[i]].r += i;
72         }
73         fft ( A, 1 ); fft ( B, 1 );
74         for ( i = 0; i < An; i ++ ) A[i] = A[i]*B[i];
75         fft ( A, -1 );
76         for ( i = 0; i <= sum[n]; i ++ ) ans[i] += (LL)(A[sum[n]+i].r+0.5);
77         for ( i = 1; i <= sum[n]; i ++ ) printf ( "%I64d\n", ans[i] );
78     }
79     return 0;
80 }
View Code

7.hdu 4609/bzoj 3513 3-idiots

题意:给你$n$条边,问你任取三条边能组成三角形的概率

按权值卷积后,把边排序

对于任意一条边你都能知道其余两条边加起来大于它的方案数

考虑第$i$条边是最长边,那么就要减去一条选大的一条选小的、两条都选大的方案数,这些都可以用组合公式算得..

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <algorithm>
 5 #include <cmath>
 6 #define LL long long
 7 using namespace std;
 8 const LL Maxn = 100010;
 9 struct cp {
10     double r, i;
11     cp ( double r=0.0, double i=0.0 ) : r(r), i(i) {}
12 }A[Maxn*4]; LL An;
13 cp operator + ( cp a, cp b ){ return cp ( a.r+b.r, a.i+b.i ); }
14 cp operator - ( cp a, cp b ){ return cp ( a.r-b.r, a.i-b.i ); }
15 cp operator * ( cp a, cp b ){ return cp ( a.r*b.r-a.i*b.i, a.r*b.i+a.i*b.r ); }
16 LL a[Maxn], n;
17 LL ans[Maxn*2];
18 const double pi = acos (-1);
19 LL _max ( LL x, LL y ){ return x > y ? x : y; }
20 void fft ( cp *a, LL op ){
21     LL i, j, k;
22     for ( i = j = 0; i < An; i ++ ){
23         if ( i < j ) swap ( a[i], a[j] );
24         k = An >> 1;
25         while ( j & k ) j -= k, k >>= 1;
26         j += k;
27     }
28     for ( i = 2; i <= An; i <<= 1 ){
29         cp wn = cp ( cos (2*pi/i), op * sin (2*pi/i) );
30         for ( j = 0; j < An; j += i ){
31             cp w = cp ( 1, 0 );
32             for ( k = j; k < j+i/2; k ++ ){
33                 cp x = a[k], y = w*a[k+i/2];
34                 a[k] = x+y; a[k+i/2] = x-y;
35                 w = w*wn;
36             }
37         }
38     }
39     if ( op == -1 ) for ( i = 0; i < An; i ++ ) A[i].r /= An;
40 }
41 int main (){
42     LL i, j, k, T;
43     scanf ( "%I64d", &T );
44     while ( T -- ){
45         scanf ( "%I64d", &n );
46         LL mx = 0;
47         for ( i = 1; i <= n; i ++ ){
48             scanf ( "%I64d", &a[i] );
49             mx = _max ( mx, a[i] );
50         }
51         An = 1;    while ( An < 2*mx+1 ) An <<= 1;
52         for ( i = 0; i < An; i ++ ) A[i].r = A[i].i = 0;
53         for ( i = 1; i <= n; i ++ ){
54             A[a[i]].r += 1;
55         }
56         fft ( A, 1 );
57         for ( i = 0; i < An; i ++ ) A[i] = A[i]*A[i];
58         fft ( A, -1 );
59         for ( i = 1; i <= mx*2; i ++ ) ans[i] = (LL)(A[i].r+0.5);
60         for ( i = 1; i <= n; i ++ ) ans[2*a[i]] --;
61         for ( i = 1; i <= mx*2; i ++ ) ans[i] /= 2;
62         for ( i = mx*2-1; i >= 1; i -- ) ans[i] += ans[i+1];
63         sort ( a+1, a+n+1 );
64         LL ret = 0;
65         for ( i = 1; i <= n; i ++ ){
66             ret += (LL)ans[a[i]+1]-n+1;
67             ret -= (LL)(n-i)*(i-1);
68             ret -= (LL)(n-i)*(n-i-1)/2;
69         }
70         LL zs = n*(n-1)*(n-2)/6;
71         printf ( "%.7lf\n", (double)ret/zs );
72     }
73     return 0;
74 }
View Code

8.hdu 5751 Eades

题意:

 

Peter有一个序列$a_1, a_2, ..., a_n$. 定义$g(l,r)$表示子序列$a_{l},a_{l+1},...,a_{r}$的最大值, $f(l,r)=\displaystyle\sum_{i=l}^{r}[a_i = g(l,r)]$. 注意$[\text{condition}] = 1$当且仅当$\text{condition}$是true, 否则$[\text{condition}] = 0$.

 

对于每个整数$k \in \{1, 2, ..., n\}$, Peter想要知道有多少整数对$l$和$r$ $(l \le r)$满足$f(l,r)=k$.

 

官方题解:

 

枚举每个数$x$, 考虑这个数成为最大值的对每个$z(\cdot)$的贡献. 对于每个数$x$, 你都可以求出若干个极大区间$[l_x,r_x]$, 表示这个$x$在区间内是最大值, 不妨假设这个$x$在$[l_x,r_x]$出现了$m$次, 每个位置分别为$p_1,p_2,...,p_m$, 那么就可以转化成一个长度为$m+1$的序列$c_0,c_2,...,c_m$. 其中$c_0=p_1-l+1$ $c_{m}=r-p_m+1$ $c_i=p_{i+1}-p_{i}, 1 \leq i < m$

 

于是对$z_k$的贡献就是$z_k=\sum_{i=0}^{m}c_i \cdot c_{i+k}$

 

这个其实就是$c_0,c_1,...,c_m$和$c_m,c_{m-1},...,c_0$的卷积, 做一次fft就好了.

时间复杂度分析:因为每一个数只有可能进入一次做fft,所以总时间均摊$O(nlogn)$

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <algorithm>
 5 #include <vector>
 6 #include <cmath>
 7 #define LL long long
 8 using namespace std;
 9 const LL Maxn = 100010;
10 struct cp {
11     double r, i;
12     cp ( double r=0.0, double i=0.0 ) : r(r), i(i) {}
13 }A[Maxn*4], B[Maxn*4]; LL An;
14 cp operator + ( cp a, cp b ){ return cp ( a.r+b.r, a.i+b.i ); }
15 cp operator - ( cp a, cp b ){ return cp ( a.r-b.r, a.i-b.i ); }
16 cp operator * ( cp a, cp b ){ return cp ( a.r*b.r-a.i*b.i, a.r*b.i+a.i*b.r ); }
17 LL a[Maxn], maxx[Maxn*4], n;
18 vector <LL> vec[Maxn];
19 LL _max ( LL x, LL y ){ return x > y ? x : y; }
20 void bulid_tree ( LL now, LL L, LL R ){
21     if ( L < R ){
22         LL mid = ( L + R ) >> 1, lc = now*2, rc = now*2+1;
23         bulid_tree ( lc, L, mid );
24         bulid_tree ( rc, mid+1, R );
25         maxx[now] = _max ( maxx[lc], maxx[rc] );
26     }
27     else maxx[now] = a[L];
28 }
29 LL query ( LL now, LL L, LL R, LL l, LL r ){
30     if ( L == l && R == r ) return maxx[now];
31     LL mid = ( L + R ) >> 1, lc = now*2, rc = now*2+1;
32     if ( r <= mid ) return query ( lc, L, mid, l, r );
33     else if ( l > mid ) return query ( rc, mid+1, R, l, r );
34     else return _max ( query ( lc, L, mid, l, mid ), query ( rc, mid+1, R, mid+1, r ) );
35 }
36 LL ans[Maxn];
37 const double pi = acos (-1);
38 void fft ( cp *a, LL op ){
39     LL i, j, k;
40     for ( i = j = 0; i < An; i ++ ){
41         if ( i < j ) swap ( a[i], a[j] );
42         k = An >> 1;
43         while ( j & k ) j -= k, k >>= 1;
44         j += k;
45     }
46     for ( i = 2; i <= An; i <<= 1 ){
47         cp wn = cp ( cos (2*pi/i), op * sin (2*pi/i) );
48         for ( j = 0; j < An; j += i ){
49             cp w = cp ( 1, 0 );
50             for ( k = j; k < j+i/2; k ++ ){
51                 cp x = a[k], y = w*a[k+i/2];
52                 a[k] = x+y; a[k+i/2] = x-y;
53                 w = w*wn;
54             }
55         }
56     }
57     if ( op == -1 ) for ( i = 0; i < An; i ++ ) a[i].r /= An;
58 }
59 void solve ( LL l, LL r ){
60     if ( l > r ) return;
61     LL i, j, k;
62     LL Max = query ( 1, 1, n, l, r );
63     LL x = lower_bound ( vec[Max].begin (), vec[Max].end (), l ) - vec[Max].begin ();
64     LL y = upper_bound ( vec[Max].begin (), vec[Max].end (), r ) - vec[Max].begin ();
65     An = 1; while ( An < 2*(y-x)+1 ) An <<= 1;
66     for ( i = 0; i < An; i ++ ) A[i].r = A[i].i = B[i].r = B[i].i = 0;
67     A[0].r = vec[Max][x]-l+1;
68     for ( i = x; i < y-1; i ++ ){
69         A[i-x+1].r = vec[Max][i+1]-vec[Max][i];
70     }
71     A[y-x].r = r-vec[Max][y-1]+1;
72     for ( i = 0; i <= y-x; i ++ ) B[i].r = A[y-x-i].r;
73     fft ( A, 1 ); fft ( B, 1 );
74     for ( i = 0; i < An; i ++ ) A[i] = A[i]*B[i];
75     fft ( A, -1 );
76     for ( i = 1; i <= y-x; i ++ ) ans[i] += (LL)(A[i+y-x].r+0.5);
77     solve ( l, vec[Max][x]-1 );
78     for ( i = x; i < y-1; i ++ ) solve ( vec[Max][i]+1, vec[Max][i+1]-1 );
79     solve ( vec[Max][y-1]+1, r );
80 }
81 int main (){
82     LL i, j, k, T;
83     scanf ( "%I64d", &T );
84     while ( T -- ){
85         scanf ( "%I64d", &n );
86         for ( i = 1; i <= n; i ++ ) vec[i].clear ();
87         for ( i = 1; i <= n; i ++ ){ scanf ( "%I64d", &a[i] ); vec[a[i]].push_back (i); }
88         bulid_tree ( 1, 1, n );
89         for ( i = 1; i <= n; i ++ ) ans[i] = 0;
90         solve ( 1, n );
91         LL ret = 0;
92         for ( i = 1; i <= n; i ++ ){
93             ret += ans[i]^i;
94         }
95         printf ( "%I64d\n", ret );
96     }
97     return 0;
98 }
View Code

 9.bzoj 4332: JSOI2012 分零食

简化题意:把$m$分到$n$个位置,允许后缀为$0$,每个位置的值为$f(i)=Oi^2+Si+U$,其中$i$是该位置分到的数,问你所有情况的权值积的和

找到三种做法:

YJQ大爷

WerKeyTom (讲道理不是很理解..)

某大神 (比较厉害的做法..)

我就是用YJQ大爷的方法的.. 他的题解讲的不是很详细就来补充一下吧

设$f_{i,j}$表示把$j$分到$i$个位置而且全部填满没有后缀$0$的答案

$g_{i,j}$表示把$j$分到$i$个位置而且一定至少有一个空没填的答案

由于$n\leq 10^8$,所以肯定是要用倍增的方法

然后就是可以这么推:$$f_{i,j}=\sum\limits_{k=1}^{j-1}f_{\frac{i}{2},j-k}\cdot f_{\frac{i}{2},k}$$

$$g_{i,j}=\sum\limits_{k=1}^{j-1}g_{\frac{i}{2},j-k}\cdot f_{\frac{i}{2},k}$$

大概就是这样..

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <algorithm>
 5 #include <cmath>
 6 using namespace std;
 7 const int Maxn = 10010;
 8 int m, P, n, O, S, U;
 9 struct cp {
10     double r, i;
11     cp ( double r=0.0, double i=0.0 ) : r(r), i(i) {}
12 }A[Maxn*4], B[Maxn*4];
13 cp operator + ( cp a, cp b ){ return cp ( a.r+b.r, a.i+b.i ); }
14 cp operator - ( cp a, cp b ){ return cp ( a.r-b.r, a.i-b.i ); }
15 cp operator * ( cp a, cp b ){ return cp ( a.r*b.r-a.i*b.i, a.r*b.i+a.i*b.r ); }
16 int f[Maxn*4], g[Maxn*4], ansf[Maxn*4], ansg[Maxn*4], An;
17 int rev[Maxn*4];
18 const double pi = acos (-1);
19 void fft ( cp *a, int op ){
20     int i, j, k;
21     for ( i = 0; i < An; i ++ ) if ( i < rev[i] ) swap ( a[i], a[rev[i]] );
22     for ( i = 2; i <= An; i <<= 1 ){
23         cp wn = cp ( cos (2*pi/i), op * sin (2*pi/i) );
24         for ( j = 0; j < An; j += i ){
25             cp w = cp ( 1, 0 );
26             for ( k = j; k < j+i/2; k ++ ){
27                 cp x = a[k], y = w*a[k+i/2];
28                 a[k] = x+y; a[k+i/2] = x-y;
29                 w = w*wn;
30             }
31         }
32     }
33     if ( op == -1 ) for ( i = 0; i < An; i ++ ) a[i].r /= An;
34 }
35 int main (){
36     int i, j, k;
37     scanf ( "%d%d%d%d%d%d", &m, &P, &n, &O, &S, &U );
38     An = 1; while ( An < 2*m+1 ) An <<= 1;
39     j = 0;
40     for ( i = 0; i < An; i ++ ){
41         rev[i] = j;
42         k = An >> 1;
43         while ( j & k ) j -= k, k >>= 1;
44         j += k;
45     }
46     g[0] = 1;
47     for ( i = 1; i <= m; i ++ ){
48         f[i] = (((O*i)%P*i)%P+(S*i)%P+U)%P;
49     }
50     ansf[0] = 1; ansg[0] = 0;
51     while (n){
52         if ( n & 1 ){
53             for ( i = 0; i <= m; i ++ ) A[i].r = ansf[i], B[i].r = g[i], A[i].i = B[i].i = 0;
54             for ( i = m+1; i < An; i ++ ) A[i].r = B[i].r = A[i].i = B[i].i = 0;
55             fft ( A, 1 ); fft ( B, 1 );
56             for ( i = 0; i < An; i ++ ) A[i] = A[i]*B[i];
57             fft ( A, -1 );
58             for ( i = 0; i <= m; i ++ ) ansg[i] = ( ansg[i] + ((int)(A[i].r+0.5))%P ) % P;
59             
60             for ( i = 0; i <= m; i ++ ) A[i].r = ansf[i], B[i].r = f[i], A[i].i = B[i].i = 0;
61             for ( i = m+1; i < An; i ++ ) A[i].r = B[i].r = A[i].i = B[i].i = 0;
62             fft ( A, 1 ); fft ( B, 1 );
63             for ( i = 0; i < An; i ++ ) A[i] = A[i]*B[i];
64             fft ( A, -1 );
65             for ( i = 0; i <= m; i ++ ) ansf[i] = ((int)(A[i].r+0.5))%P;
66         }
67         for ( i = 0; i <= m; i ++ ) A[i].r = f[i], B[i].r = g[i], A[i].i = B[i].i = 0;
68         for ( i = m+1; i < An; i ++ ) A[i].r = B[i].r = A[i].i = B[i].i = 0;
69         fft ( A, 1 ); fft ( B, 1 );
70         for ( i = 0; i < An; i ++ ) A[i] = A[i]*B[i];
71         fft ( A, -1 );
72         for ( i = 0; i <= m; i ++ ) g[i] = ( g[i] + ((int)(A[i].r+0.5))%P ) % P;
73         
74         for ( i = 0; i <= m; i ++ ) A[i].r = f[i], A[i].i = 0;
75         for ( i = m+1; i < An; i ++ ) A[i].r = A[i].i = 0;
76         fft ( A, 1 );
77         for ( i = 0; i < An; i ++ ) A[i] = A[i]*A[i];
78         fft ( A, -1 );
79         for ( i = 0; i <= m; i ++ ) f[i] = ((int)(A[i].r+0.5))%P;
80         n >>= 1;
81     }
82     printf ( "%d\n", (ansf[m]+ansg[m])%P );
83     return 0;
84 }
View Code

 10.bzoj 3456: 城市规划

这是一道比较厉害的NTT题目.. 我不是很会解释,建议去看看WerKeyTom的blog

这道题的时间也比较坑,我卡了很久的常数才过..

 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <algorithm>
 5 #include <ctime>
 6 #include <iostream>
 7 #define LL long long
 8 using namespace std;
 9 const LL Maxn = 130010;
10 const LL Mod = 1004535809;
11 const LL G = 3;
12 LL A[Maxn*4], B[Maxn*4], An;
13 LL pow ( LL x, LL k ){
14     LL ret = 1;
15     while (k){
16         if ( k & 1 ) ret = (ret*x)%Mod;
17         x = (x*x)%Mod;
18         k >>= 1;
19     }
20     return ret;
21 }
22 LL v[Maxn], f[Maxn], jc[Maxn], fjc[Maxn], n, inv[Maxn], cf[Maxn*4], fcf[Maxn*4], p[Maxn];
23 void dft ( LL *a, LL op ){
24     LL i, j, k;
25     for ( i = j = 0; i < An; i ++ ){
26         if ( i < j ) swap ( a[i], a[j] );
27         k = An >> 1;
28         while ( j & k ) j -= k, k >>= 1;
29         j += k;
30     }
31     for ( i = 2; i <= An; i <<= 1 ){
32         LL wn = op > 0 ? cf[i] : fcf[i];
33         LL w = 1;
34         for ( k = 0; k < i/2; k ++ ){
35             for ( j = 0; j < An; j += i ){
36                 LL x = a[k+j], y = (w*a[k+j+i/2])%Mod;
37                 a[k+j] = (x+y)%Mod; a[k+j+i/2] = (x-y+Mod)%Mod;
38             }
39             w = (w*wn)%Mod;
40         }
41     }
42     if ( op == -1 ) for ( i = 0; i < An; i ++ ) a[i] = (a[i]*inv[An])%Mod;
43 }
44 void cdq ( LL l, LL r ){
45     if ( l == r ){ f[l] = (v[l]-(jc[l-1]*f[l])%Mod+Mod)%Mod; return; }
46     LL mid = ( l + r ) >> 1, i;
47     cdq ( l, mid );
48     LL len = r-l+1;
49     An = 1; while ( An < 2*len+1 ) An <<= 1;
50     for ( i = 0; i < An; i ++ ) A[i] = B[i] = 0;
51     for ( i = 0; i < len; i ++ ) B[i] = p[i];
52     for ( i = 0; i < mid-l+1; i ++ ) A[i] = (f[i+l]*fjc[i+l-1])%Mod; 
53     dft ( A, 1 ); dft ( B, 1 );
54     for ( i = 0; i < An; i ++ ) A[i] = (A[i]*B[i])%Mod;
55     dft ( A, -1 );
56     for ( i = mid+1; i <= r; i ++ ) f[i] = (f[i]+A[i-l])%Mod;
57     cdq ( mid+1, r );
58 }
59 int c[Maxn];
60 int main (){
61     LL i, j, k;
62     scanf ( "%lld", &n );
63     jc[0] = 1; fjc[0] = 1; v[0] = 1;
64     c[0] = 1; for ( i = 1; i <= n; i ++ ) c[i] = (c[i-1]*2)%Mod;
65     for ( i = 1; i <= n; i ++ ){
66         v[i] = ( v[i-1] * c[i-1] ) % Mod;
67         jc[i] = (jc[i-1]*i)%Mod;
68     }
69     fjc[n] = pow ( jc[n], Mod-2 );
70     for ( i = n-1; i >= 1; i -- ){
71         fjc[i] = (fjc[i+1]*(i+1))%Mod;
72         p[i] = (v[i]*fjc[i])%Mod;
73     }
74     An = 1; while ( An < 2*n+1 ) An <<= 1;
75     for ( i = 2; i <= An; i <<= 1 ) cf[i] = pow ( G, (Mod-1)/i ), fcf[i] = pow ( G, Mod-1-(Mod-1)/i ), inv[i] = pow ( i, Mod-2 );
76     cdq ( 1, n );
77     printf ( "%lld\n", f[n] );
78     return 0;
79 }
View Code

 


11.bzoj 4555: [Tjoi2016&Heoi2016]求和

 

把公式化一下基本就出来了,这里给出几个公式:

斯特林数化简公式:$$S(n,m)=\dfrac{1}{m!}\sum\limits_{k=0}^{m}(-1)^k\cdot C_m^k\cdot (m-k)^n$$

等比数列求和公式:$$Sum=\dfrac{a(q^k-1)}{q-1}$$

那么最后化成的公式为:$$f(n)=\sum\limits_{j=0}^n2^j\cdot j!\sum\limits_{k=0}^jg(k)\cdot h(j-k)$$

$$g(i)=\dfrac{(-1)^i}{i!},\ h(i)=\dfrac{\sum_{k=0}^{n}i^k}{i!}$$

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <algorithm>
 5 #define LL long long
 6 using namespace std;
 7 const LL Maxn = 100010;
 8 const LL Mod = 998244353;
 9 const LL G = 3;
10 LL jc[Maxn], inv[Maxn], invn;
11 LL pow ( LL x, LL k ){
12     LL ret = 1;
13     while (k){
14         if ( k & 1 ) ret = (ret*x)%Mod;
15         x = (x*x)%Mod;
16         k >>= 1;
17     }
18     return ret;
19 }
20 LL A[Maxn*4], B[Maxn*4], An;
21 LL n;
22 LL dft ( LL *a, LL op ){
23     LL i, j, k;
24     for ( i = j = 0; i < An; i ++ ){
25         if ( i < j ) swap ( a[i], a[j] );
26         k = An >> 1;
27         while ( j & k ) j -= k, k >>= 1;
28         j += k;
29     }
30     for ( i = 2; i <= An; i <<= 1 ){
31         LL wn = pow ( G, op > 0 ? (Mod-1)/i : Mod-1-(Mod-1)/i );
32         for ( j = 0; j < An; j += i ){
33             LL w = 1;
34             for ( k = j; k < j+i/2; k ++ ){
35                 LL x = a[k], y = (w*a[k+i/2])%Mod;
36                 a[k] = (x+y)%Mod; a[k+i/2] = (x-y+Mod)%Mod;
37                 w = (w*wn)%Mod;
38             }
39         }
40     }
41     if ( op == -1 ) for ( i = 0; i < An; i ++ ) a[i] = (a[i]*invn)%Mod;
42 }
43 int main (){
44     LL i, j, k;
45     scanf ( "%lld", &n );
46     An = 1; while ( An < 2*n+1 ) An <<= 1;
47     jc[0] = jc[1] = 1;
48     for ( i = 2; i <= n; i ++ ) jc[i] = (jc[i-1]*i)%Mod;
49     inv[n] = pow ( jc[n], Mod-2 );
50     for ( i = n-1; i >= 1; i -- ) inv[i] = (inv[i+1]*(i+1))%Mod;
51     A[0] = 1;
52     for ( i = 1; i <= n; i ++ ) A[i] = i % 2 == 1 ? Mod-inv[i] : inv[i];
53     B[0] = 1; B[1] = n+1;
54     for ( i = 2; i <= n; i ++ ) B[i] = ( ( ( pow ( i, n+1 ) - 1 ) * pow ( i-1, Mod-2 ) ) % Mod * inv[i] ) % Mod;
55     invn = pow ( An, Mod-2 );
56     dft ( A, 1 ); dft ( B, 1 );
57     for ( i = 0; i < An; i ++ ) A[i] = (A[i]*B[i])%Mod;
58     dft ( A, -1 );
59     LL s = 1, ans = 0;
60     for ( i = 0; i <= n; i ++ ){
61         ans = ( ans + ((s*jc[i])%Mod*A[i])%Mod ) % Mod;
62         s = (s*2)%Mod;
63     }
64     printf ( "%lld\n", ans );
65     return 0;
66 }
67 
View Code

 


参考资料

picks的blog

posted @ 2016-10-26 22:02  Ra1nbow  阅读(201)  评论(1编辑  收藏  举报