題目描述spa
給出一個長度爲 $n$ 的序列 $\{s\}$ ,對於全部知足如下條件的五元組 $(a,b,c,d,e)$ :code
求 $f(s_a|s_b)\times f(s_c)\times f(s_d\text{^}s_e)$ 的和模 $10^9+7$,其中 $f(i)$ 表示斐波那契數列的第 $i$ 項( $f(0)=0,f(1)=1$ )。orm
題解blog
FWT+FST(Fast-Subset-Transform)get
顯然是求 $cnt[s_a]$ 和 $cnt[s_b]$ 的子集卷積得出 $cnt[s_a|s_b]$ ,求 $cnt[s_d]$ 和 $cnt[s_e]$ 的異或卷積得出 $cnt[s_d\text{^}s_e]$ ,而後求 $cnt[s_a|s_b]\times f[s_a|s_b]$ 、$cnt[s_c]\times f[s_c]$ 、$cnt[s_d\text{^}s_e]\times f[s_d\text{^}s_e]$ 的與卷積,與卷積的 $2^i$ 項之和即爲答案。it
(子集卷積:$c$ 是 $a$ 和 $b$ 的子集卷積,當且僅當:$c[i]=\sum\limits_{j|k=i,j\&k=0}a[j]\times b[k]$ ,直觀理解上等價於 $c[i]=\sum\limits_{j\in i}a[j]\times b[i-j]$ ,故稱子集卷積)io
異或卷積和與卷積能夠直接使用FWT計算。ast
子集卷積的計算方法能夠參考vfk集訓隊論文中提到的佔位多項式法:form
$j|k=i,j\&k=0$ 等價於 $j|k=i,|j|+|k|=|i|$ 。class
所以求 $c'[p][i]=\sum\limits_{j|k=i,|j|+|k|=p}a[j]\times b[k]=\sum\limits_{j|k=i,|j|+|k|=p}a'[|j|][j]\times b'[|k|][k]=\sum\limits_{j|k=i,q+r=p}a'[q][j]+b'[r][k]$ ,那麼 $c[i]=c'[|i|][i]$ 。
其中 $|i|$ 表示 $i$ 集合的大小,即 $i$ 二進制中 $1$ 的個數。$a'[|i|][i]=a[i]$ ,其他爲0;$b'$ 同理。
那麼咱們對每個 $a'[q][]$ 和 $b'[r][]$ 分別求DWT,而後進行相似揹包合併的卷積,再求IDWT便可。這個部分的時間複雜度爲 $O(2^{17}·17^2)$ 。
所以總的時間複雜度爲 $O(2^{17}·17^2+2^{17}·17·常數)$ 。
這裏我腦殘了... $cnt[s_a,s_b,s_c,s_d,s_e]$ 都是同樣的,所以能夠減小DWT的次數... 無論了反正A了...
#include <cstdio> #include <algorithm> #define N 131100 #define mod 1000000007 #define inv 500000004 using namespace std; typedef long long ll; int s[1000010] , cnt[N]; ll fib[N] , a[18][N] , b[18][N] , c[N] , d[N] , e[N] , f[18][N]; int main() { int n , m = 1 , mx = 0 , k , i , j; ll t , ans = 0; scanf("%d" , &n); for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &s[i]) , mx = max(mx , s[i]); while(m <= mx) m <<= 1; fib[1] = 1; for(i = 2 ; i < m ; i ++ ) fib[i] = (fib[i - 1] + fib[i - 2]) % mod; for(i = 1 ; i < m ; i ++ ) cnt[i] = cnt[i - (i & -i)] + 1; for(i = 1 ; i <= n ; i ++ ) a[cnt[s[i]]][s[i]] ++ , b[cnt[s[i]]][s[i]] ++ , c[s[i]] ++ , d[s[i]] ++ , e[s[i]] ++ ; for(i = 0 ; i < m ; i ++ ) c[i] = c[i] * fib[i] % mod; for(i = 1 ; i < m ; i <<= 1) for(j = 0 ; j < m ; j ++ ) if(i & j) t = d[j] , d[j] = (d[j - i] - t + mod) % mod , d[j - i] = (d[j - i] + t) % mod; for(i = 1 ; i < m ; i <<= 1) for(j = 0 ; j < m ; j ++ ) if(i & j) t = e[j] , e[j] = (e[j - i] - t + mod) % mod , e[j - i] = (e[j - i] + t) % mod; for(i = 0 ; i < m ; i ++ ) d[i] = d[i] * e[i] % mod; for(i = 1 ; i < m ; i <<= 1) for(j = 0 ; j < m ; j ++ ) if(i & j) t = d[j] , d[j] = (d[j - i] - t + mod) * inv % mod , d[j - i] = (d[j - i] + t) * inv % mod; for(i = 0 ; i < m ; i ++ ) d[i] = d[i] * fib[i] % mod; for(k = 0 ; k <= cnt[m - 1] ; k ++ ) { for(i = 1 ; i < m ; i <<= 1) for(j = 0 ; j < m ; j ++ ) if(i & j) a[k][j] = (a[k][j] + a[k][j - i]) % mod; for(i = 1 ; i < m ; i <<= 1) for(j = 0 ; j < m ; j ++ ) if(i & j) b[k][j] = (b[k][j] + b[k][j - i]) % mod; } for(i = 0 ; i <= cnt[m - 1] ; i ++ ) for(j = 0 ; j <= cnt[m - 1] - i ; j ++ ) for(k = 0 ; k < m ; k ++ ) f[i + j][k] = (f[i + j][k] + a[i][k] * b[j][k]) % mod; for(k = 0 ; k <= cnt[m - 1] ; k ++ ) for(i = 1 ; i < m ; i <<= 1) for(j = 0 ; j < m ; j ++ ) if(i & j) f[k][j] = (f[k][j] - f[k][j - i] + mod) % mod; for(i = 0 ; i < m ; i ++ ) e[i] = f[cnt[i]][i] * fib[i] % mod; for(i = 1 ; i < m ; i <<= 1) for(j = 0 ; j < m ; j ++ ) if(i & j) c[j - i] = (c[j - i] + c[j]) % mod; for(i = 1 ; i < m ; i <<= 1) for(j = 0 ; j < m ; j ++ ) if(i & j) d[j - i] = (d[j - i] + d[j]) % mod; for(i = 1 ; i < m ; i <<= 1) for(j = 0 ; j < m ; j ++ ) if(i & j) e[j - i] = (e[j - i] + e[j]) % mod; for(i = 0 ; i < m ; i ++ ) c[i] = c[i] * d[i] % mod * e[i] % mod; for(i = 1 ; i < m ; i <<= 1) for(j = 0 ; j < m ; j ++ ) if(i & j) c[j - i] = (c[j - i] - c[j] + mod) % mod; for(i = 1 ; i < m ; i <<= 1) ans = (ans + c[i]) % mod; printf("%lld\n" , ans); return 0; }