這題我是看官方解寫的,但他寫的很不清楚,這裡紀錄一下詳細,雖然應該有不用用到這種東西的作法......
題目簡單來說就是要求向量A^t\cdot e_0是多少,其中A是一個n\times n的矩陣,滿足他的(i,j)項為b[bit(i\; xor\; j)](其中用bit(x)代表x的二進制中1的個數),e_0則是一個給定的n維向量。如果A有n個線性獨立的 eigenvector,設其為v_0,...,v_{n-1},並且對應的 eigenvalue 為\lambda _0,...,\lambda _{n-1},那麼可以先把e_0拆成v_i的組合,也就是令c_0,...,c_{n-1}滿足\displaystyle e_0=\sum_{i=0}^{n-1} c_iv_i,那麼所求就會\displaystyle =A^t\sum_{i=0}^{n-1} c_iv_i=\sum_{i=0}^{n-1}c_iA^tv_i =\sum_{i=0}^{n-1} c_i\lambda _i^tv_i。所以接下來的步驟就是先求出c_i和\lambda _i,算出v_i前面的係數之後再想辦法組合起來。
神奇的是A這個矩陣的確有n個 eigenvector ,並且v_i的第j項恰好等於(-1)^{bit(i\& j)}(這篇中所有的證明都放在最後)。記矩陣H=\begin{bmatrix}v_0 & v_1 & \dots & v_{n-1}\end{bmatrix},並且回到\displaystyle e_0=\sum_{i=0}^{n-1} c_iv_i這個式子,他就會等價於
e_0=H\cdot \begin{bmatrix}c_0\\c_1\\\vdots\\c_{n-1}\end{bmatrix},所以\begin{bmatrix}c_0\\c_1\\\vdots\\c_{n-1}\end{bmatrix}=H^{-1}e_0。又因為H是對稱的,還有v_0,...,v_{n-1}兩兩正交,所以可以得到H\cdot H=n\cdot I,也就是H^{-1}=\frac{1}{n}H,所以這裡本質上就是要算H乘以一個向量得到的向量是多少。
我們把求H乘上一個向量的作法留到後面,先來看\lambda _i的求法。因為Av_i=\lambda _iv_i,所以我們只要比對兩邊第一項的值就好了。可以得到\displaystyle \sum_{k=0}^{n-1} b[bit(k)](-1)^{bit(i\& k)}=\lambda _i (這裡代入了A的第(0,k)項為b[bit(0\; xor \; k)]=b[bit(k)],還有v_i的第k項為(-1)^{bit(i\& k)})。令另一個n維向量d的第k項等於b[bit(k)],那麼上式可以改寫為\lambda _i = d \cdot v_i,因此把n條式子(i=0,...,n-1)寫在一起就可以變成\begin{bmatrix}\lambda _0\\\lambda _1\\\vdots\\\lambda _{n-1}\end{bmatrix}=\begin{bmatrix}v_0^T\\v_1^T\\\vdots\\v_{n-1}^T\end{bmatrix}\cdot d=H\cdot d,因此這裡也會變成求H乘上一個向量的問題。
最後則是求出所求的向量\displaystyle \sum_{i=0}^{n-1} c_i\lambda _i^tv_i,他可以改寫成\begin{bmatrix}v_0 & v_1 & \dots & v_{n-1}\end{bmatrix}\cdot \begin{bmatrix}c_0\lambda _0^t \\ c_1\lambda _1^t\\\vdots\\c_{n-1}\lambda _{n-1}^t \end{bmatrix},因此一樣是求H乘一個向量,所以現在問題剩下這件事要怎麼作了。事實上我們可以用遞迴來定義H,定義H_0代表只有一個1的1\times 1的矩陣,H_i則定義為\begin{bmatrix}H_{i-1} & H_{i-1} \\ H_{i-1} & -H_{i-1}\end{bmatrix},其中他是2^i\times 2^i的矩陣,那麼這邊的H就會是H_m(這不難由前面H的定義得出)。有了這個遞迴式就能幫助我們在O(nlogn)的時間內算出H乘上一個向量是多少了。考慮「求出H_i\begin{bmatrix}u_0 \\ \vdots \\ u_{2^i-1} \end{bmatrix}」這個問題,由上面的遞迴式可得他就等於\begin{bmatrix}H_{i-1} & H_{i-1} \\ H_{i-1} & -H_{i-1}\end{bmatrix}\begin{bmatrix}u_0 \\ \vdots \\ u_{2^i-1} \end{bmatrix},因此我們先遞迴下去算出H_{i-1}\begin{bmatrix}u_0 \\ \vdots \\ u_{2^{i-1}-1} \end{bmatrix}和H_{i-1}\begin{bmatrix}u_{2^{i-1}} \\ \vdots \\ u_{2^i-1} \end{bmatrix},假設分別得到U_1和U_2,那麼不難得到所求就會等於\begin{bmatrix}U_1+U_2 \\ U_1-U_2 \end{bmatrix},也就是能在線性的時間內合併兩個子問題,因此複雜度是O(nlogn)。他的寫法就有點類似FFT,但這個簡潔多了。
=======================================================================
最後補上前面沒證的證明,一個是為什麼v_i們就會是A的 eigenvector ,還有為什麼v_i們兩兩正交。兩兩正交比較好證,我們要證明v_i\cdot v_j=0(其中i\neq j),這等價於\displaystyle \sum_{k=0}^{n-1} (-1)^{bit(i\& k)+bit(j\& k)}=0,假設i,j的第x個 bit 不同,那麼考慮上式中將第k項和第k\; xor \; 2^x項配對,不難證明這兩項一個是1一個是-1,因此總和為0。最後證明v_i為 eigenvector ,考慮Av_i=\lambda _i v_i這式子兩邊的第j項,可以得到\displaystyle \sum_{k=0}^{n-1}b[bit(j\; xor \; k)](-1)^{bit(i\& k)}=\lambda _i (-1)^{bit(i\& j)},我們要證明的是當j跑遍0,...,n-1時\lambda _i 都會是固定的。考慮左式中b[z]的係數,可以得到他會是\displaystyle \sum_x (-1)^{bit(i\& x)},其中x跑遍所有和j差了z個 bit 的數(也就是bit(j\; xor \; x)=z)。考慮把j的第r個 bit 改掉時b[z]的係數會如何變化,分成i的第r個 bit 是0或1兩種情況,而不管是哪種情況在計算b[z]的那條式子中的x們必須把他們的第r個 bit 都改掉才會滿足他和新的j差z個 bit ,因此當i的第r個 bit 是1的時候,對於所有的b[z]係數中的(-1)^{bit(i\& x)}都會乘上一個負號,否則係數都不會變。回到我們要證明的那條式子,剛才說明了當j的某個 bit 改變時左式會發生什麼事,而右式中的(-1)^{bit(i\& j)}也恰好會發生一樣的改變,當j變動的 bit 在i那邊也是1時則變為-1倍,否則不變,這就得到了當j跑遍所有0,...,n-1的數時\lambda _i都會是定值,因此v_i為A的 eigenvector。
code :
#include<bits/stdc++.h> #define LL long long #define bit(x) __builtin_popcount(x) using namespace std; const int maxm=20 , maxn=1<<maxm ; LL MOD ; inline LL llmul(LL a,LL b) { LL ret=(a*b-(LL)((long double)a*b/MOD)*MOD)%MOD ; if(ret<0) ret+=MOD ; return ret ; } LL power(LL x,LL n) { if(n<=1) return n ? x : 1 ; LL t=power(x,n/2) ; if(n%2) return llmul(llmul(t,t),x) ; else return llmul(t,t) ; } void FWT(LL *a,int n) { if(n==1) return ; int n2=n>>1 ; FWT(a,n2) ; FWT(a+n2,n2) ; for(int i=0;i<n2;i++) { LL x=a[i] , y=a[i+n2] ; a[i]=(x+y)%MOD ; a[i+n2]=(x-y+MOD)%MOD ; } } LL e0[maxn],b[maxm+10],c[maxn] ; LL ev[maxn] ; /// eigenvalue main() { int n,m ; LL t,p ; scanf("%d%lld%lld",&m,&t,&p) ; n=1<<m ; MOD=n*p ; for(int i=0;i<n;i++) scanf("%lld",&e0[i]) ; for(int i=0;i<=m;i++) scanf("%lld",&b[i]) ; for(int i=0;i<n;i++) ev[i]=b[bit(i)]%MOD ; FWT(e0,n) ; FWT(ev,n) ; for(int i=0;i<n;i++) c[i]=llmul(e0[i],power(ev[i],t)) ; FWT(c,n) ; for(int i=0;i<n;i++) printf("%lld\n",((c[i]+MOD)%MOD)>>m) ; }
沒有留言:
張貼留言