先簡短幾句話說說FFT....
多項式可用系數和點值表示,n個點可確定一個次數小於n的多項式。
多項式乘積為 f(x)*g(x),顯然若已知f(x), g(x)的點值,O(n)可求得多項式乘積的點值。
我們所需要的就是O(nlogn)快速地將兩個系數多項式表示成點值多項式,O(n)求得乘積的點值表示后O(nlogn)還原成系數多項式。
這里就需要套FFT板子了...
FFT中取n個單位根,需要n是2的冪。
又因為n個點可確定一個次數小於n的多項式,所以n > 乘積多項式的最高次數。
以上。
HDU4609 n個木棍任取三根能組成三角形的概率。
數組開小莫名T,WA.

1 #include <bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int N = 4e5+10; 5 struct comp{ 6 double r,i;comp(double _r=0,double _i=0){r=_r;i=_i;} 7 comp operator+(const comp x){return comp(r+x.r,i+x.i);} 8 comp operator-(const comp x){return comp(r-x.r,i-x.i);} 9 comp operator*(const comp x){return comp(r*x.r-i*x.i,r*x.i+i*x.r);} 10 }; 11 const double pi=acos(-1.0); 12 void FFT(comp a[],int n,int t){ 13 for(int i=1,j=0;i<n-1;i++){ 14 for(int s=n;j^=s>>=1,~j&s;); 15 if(i<j)swap(a[i],a[j]); 16 } 17 for(int d=0;(1<<d)<n;d++){ 18 int m=1<<d,m2=m<<1; 19 double o=pi/m*t;comp _w(cos(o),sin(o)); 20 for(int i=0;i<n;i+=m2){ 21 comp w(1,0); 22 for(int j=0;j<m;j++){ 23 comp &A=a[i+j+m],&B=a[i+j],t=w*A; 24 A=B-t;B=B+t;w=w*_w; 25 } 26 } 27 } 28 if(t==-1)for(int i=0;i<n;i++)a[i].r/=n; 29 } 30 comp x[N]; 31 ll num[N], sum[N]; 32 int main(){ 33 int T; scanf("%d", &T); 34 while(T--){ 35 memset(num, 0, sizeof num); 36 int n, u, maxnum = -1; scanf("%d", &n); 37 for(int i = 0; i < n; i++){ 38 scanf("%d", &u); 39 maxnum = max(maxnum, u), num[u]++; 40 } 41 int len = 1; 42 while(len <= maxnum*2) len <<= 1; 43 for(int i = 0; i < len; i++) 44 x[i] = comp(num[i], 0); 45 FFT(x, len, 1); 46 for(int i = 0; i < len; i++) 47 x[i] = x[i]*x[i]; 48 FFT(x, len , -1); 49 for(int i = 0; i < len; i++) 50 sum[i] = x[i].r+0.5; 51 for(int i = 0; i < len; i+=2) 52 sum[i] -= num[i>>1];//去掉兩次取的木棍相同的 53 for(int i = 0; i < len; i ++) 54 sum[i] >>= 1;//算了2次 55 for(int i = 1; i < len; i++) 56 sum[i] += sum[i-1]; 57 ll tot = (ll)n*(n-1)*(n-2)/6, ans = tot; 58 for(int i = 0; i <= maxnum; i++) 59 ans -= num[i]*sum[i];//去掉不能組成三角形的 60 printf("%.7f\n", 1.0*ans/tot); 61 } 62 return 0; 63 }
LA4671 給出A串與B串,只含小寫字母a、b。問:A串中有多少本質不同的子串滿足 與B串長度相同 且 與B串相對應位置字符不同的數量小於k。
題解:a做1,b做0。將B倒着來一遍和A做卷積,可得有多少位置A串與B串都是a。a做0,b做1再來一遍即可。
hash!37做基1000173169做模會沖突!1e9+7做基1e9+9做模也會沖突!雙哈希可以過,37做基2^64做模可過,37做基100000000173169LL做模也可過....

1 #include <bits/stdc++.h> 2 #define ull unsigned long long 3 using namespace std; 4 const int N = 4e5+10; 5 struct comp{ 6 double r,i;comp(double _r=0,double _i=0){r=_r;i=_i;} 7 comp operator+(const comp x){return comp(r+x.r,i+x.i);} 8 comp operator-(const comp x){return comp(r-x.r,i-x.i);} 9 comp operator*(const comp x){return comp(r*x.r-i*x.i,r*x.i+i*x.r);} 10 }; 11 const double pi=acos(-1.0); 12 void FFT(comp a[],int n,int t){ 13 for(int i=1,j=0;i<n-1;i++){ 14 for(int s=n;j^=s>>=1,~j&s;); 15 if(i<j)swap(a[i],a[j]); 16 } 17 for(int d=0;(1<<d)<n;d++){ 18 int m=1<<d,m2=m<<1; 19 double o=pi/m*t;comp _w(cos(o),sin(o)); 20 for(int i=0;i<n;i+=m2){ 21 comp w(1,0); 22 for(int j=0;j<m;j++){ 23 comp &A=a[i+j+m],&B=a[i+j],t=w*A; 24 A=B-t;B=B+t;w=w*_w; 25 } 26 } 27 } 28 if(t==-1)for(int i=0;i<n;i++)a[i].r/=n; 29 } 30 comp x[N], y[N]; 31 char a[100010], b[100010]; 32 int ans[N]; 33 //hash 34 ull xp[122222] = {1}, H[122222]; 35 void initHash(){ 36 H[0] = a[0]; 37 for(int i = 1; a[i]; i++) 38 H[i] = H[i-1]*31uLL+a[i]; 39 } 40 ull askHash(int l, int r){ 41 if(l == 0) return H[r]; 42 return H[r]-H[l-1]*xp[r-l+1]; 43 } 44 45 int main(){ 46 for(int i = 1; i < 122222; i++) xp[i] = xp[i-1]*31uLL; 47 48 int ca = 1, K; 49 while(scanf("%d", &K), K != -1){ 50 scanf(" %s %s", a, b); 51 int lenA = strlen(a), lenB = strlen(b), len = 1; 52 if(lenA < lenB){ 53 printf("Case %d: %d\n", ca++, 0); 54 continue ; 55 } 56 57 for(int i = 0; i < lenB-1-i; i++) 58 swap(b[i], b[lenB-1-i]); 59 while(len <= lenA+lenB) len <<= 1; 60 61 for(int i = 0; i < len; i++){ 62 x[i] = comp(i < lenA? (a[i] == 'a'): 0, 0); 63 y[i] = comp(i < lenB? (b[i] == 'a'): 0, 0); 64 } 65 FFT(x, len, 1); 66 FFT(y, len, 1); 67 for(int i = 0; i < len; i++) 68 x[i] = x[i]*y[i]; 69 FFT(x, len, -1); 70 for(int i = 0; i < len; i++) 71 ans[i] = x[i].r+0.5; 72 73 for(int i = 0; i < len; i++){ 74 x[i] = comp(i < lenA? (a[i] == 'b') : 0, 0); 75 y[i] = comp(i < lenB? (b[i] == 'b') : 0, 0); 76 } 77 FFT(x, len, 1); 78 FFT(y, len, 1); 79 for(int i = 0; i < len; i++) 80 x[i] = x[i]*y[i]; 81 FFT(x, len, -1); 82 for(int i = 0; i < len; i++) 83 ans[i] += x[i].r+0.5; 84 85 initHash(); 86 set<ull> se; 87 for(int i = lenB-1; i < lenA; i++) 88 if(ans[i] >= lenB-K) 89 se.insert(askHash(i-lenB+1, i)); 90 printf("Case %d: %d\n", ca++, (int)se.size()); 91 } 92 return 0; 93 }
=================================分割線===================================
NTT
NTT與FFT類似,FFT用復數形式會有精度損失,而NTT則是在整數域內取模意義下,無精度損失。
如果 P = r⋅2k +1 是個素數,G是模P下的一個原根,那么在mod P 意義下,可以處理 2k 以內規模的數據 。
G在模P下的階為 P-1,即 r⋅2k
那么Gr 在模P下的階為2k ,這里的 Gr 即等價於FFT里的wn .
那么我們用模P下的卷積運算就不會產生精度損失。
P = 998244353 = 119*223+1, 能夠處理223 = 8e6+ 規模的數據,原根為3.
P = 1004535809 = 479*221+1, 能夠處理221 = 2e6+ 規模的數據,原根為3, 且 1004535809 加起來不會爆 int.
NTT能解決模數 P = r⋅2k +1 的問題,那么如何解決模任意數呢?
先前的 NTT資料 里有提到,
即用多個小素數跑NTT,最后用中國剩余定理求出 n(m-1)2 內滿足條件的唯一值,當 各個素數積 > n(m-1)2 時中國剩余定理后顯然可取得唯一值。
=================================分割線===================================
Ck = ∑i⊕j=k (Ai*Bj), ⊕是某種運算符號。當⊕是+時,即是傅里葉變換;當⊕是^, &, |等某種位運算時,即是FWT快速沃爾什變換。
FFT中,數組長度要大於結果的最高次冪,高位補0;FWT時,數組長度需要是2的整數次冪,不足補0。
原理不是怎么重要...
模板套用即可。

1 //快速沃爾什變換 2 void FWT(int*a,int n){ 3 for(int d=1;d<n;d<<=1)for(int m=d<<1,i=0;i<n;i+=m)for(int j=0;j<d;j++){ 4 int x=a[i+j],y=a[i+j+d]; 5 //xor:a[i+j]=x+y,a[i+j+d]=x−y; 6 //and:a[i+j]=x+y; 7 //or:a[i+j+d]=x+y; 8 } 9 } 10 void UFWT(int*a,int n){ 11 for(int d=1;d<n;d<<=1)for(int m=d<<1,i=0;i<n;i+=m)for(int j=0;j<d;j++){ 12 int x=a[i+j],y=a[i+j+d]; 13 //xor:a[i+j]=(x+y)/2,a[i+j+d]=(x−y)/2; 14 //and:a[i+j]=x−y; 15 //or:a[i+j+d]=y−x; 16 } 17 }
防溢出可mod 1e9+7大質數,則除以2的時候乘2的逆元。
FWT后,相乘,UFWT回去即可。