写在前面的..
感觉自己是应该学点新东西了.. 所以就挖个大坑,去学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 ( "
" );
49 return 0;
50 }
2.bzoj 3527: [Zjoi2014]力
这题在bzoj上没有题面,百度一下就行了,给个链接吧
化简一下公式:$$E_i=sumlimits_{j<i}dfrac{q_j}{(i-j)^2}-sumlimits_{j>i}dfrac{q_j}{(i-j)^2}$$
然后你设$g_i=dfrac{1}{i^2}$,只考虑前面一部分的公式就可化成:$$E_i=sumlimits_{j<i}q_j imes 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
", ans[i] );
62 return 0;
63 }
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
", ret );
87 return 0;
88 }
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+bgeq 2sqrt{ab}$$
那么这个时间就是大于等于$2nsqrt{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
", ans );
80 return 0;
81 }
82
5.hdu 5730 Shell Necklace
先把原题公式化简一下就是:$$dp_i=sumlimits_{j=1}^{i-1}a_{i-j} imes 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
", f[n] );
76 }
77 return 0;
78 }
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
", 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
", ans[i] );
78 }
79 return 0;
80 }
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
", (double)ret/zs );
72 }
73 return 0;
74 }
8.hdu 5751 Eades
题意:
Peter有一个序列$a_1, a_2, ..., a_n$. 定义$g(l,r)$表示子序列$a_{l},a_{l+1},...,a_{r}$的最大值, $f(l,r)=displaystylesum_{i=l}^{r}[a_i = g(l,r)]$. 注意$[ ext{condition}] = 1$当且仅当$ ext{condition}$是true, 否则$[ ext{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
", ret );
96 }
97 return 0;
98 }
9.bzoj 4332: JSOI2012 分零食
简化题意:把$m$分到$n$个位置,允许后缀为$0$,每个位置的值为$f(i)=Oi^2+Si+U$,其中$i$是该位置分到的数,问你所有情况的权值积的和
找到三种做法:
WerKeyTom (讲道理不是很理解..)
某大神 (比较厉害的做法..)
我就是用YJQ大爷的方法的.. 他的题解讲的不是很详细就来补充一下吧
设$f_{i,j}$表示把$j$分到$i$个位置而且全部填满没有后缀$0$的答案
$g_{i,j}$表示把$j$分到$i$个位置而且一定至少有一个空没填的答案
由于$nleq 10^8$,所以肯定是要用倍增的方法
然后就是可以这么推:$$f_{i,j}=sumlimits_{k=1}^{j-1}f_{frac{i}{2},j-k}cdot f_{frac{i}{2},k}$$
$$g_{i,j}=sumlimits_{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 ", (ansf[m]+ansg[m])%P ); 83 return 0; 84 }
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 ", f[n] ); 78 return 0; 79 }
11.bzoj 4555: [Tjoi2016&Heoi2016]求和
把公式化一下基本就出来了,这里给出几个公式:
斯特林数化简公式:$$S(n,m)=dfrac{1}{m!}sumlimits_{k=0}^{m}(-1)^kcdot C_m^kcdot (m-k)^n$$
等比数列求和公式:$$Sum=dfrac{a(q^k-1)}{q-1}$$
那么最后化成的公式为:$$f(n)=sumlimits_{j=0}^n2^jcdot j!sumlimits_{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 ", ans ); 65 return 0; 66 } 67