子集卷積小結

廢話

是在 NOI Online 3 上被強行安利的科技……

模板題在你谷上都只有 115115 人過qwq……

正題

是用來做這樣的卷積的:
cn=i&j=0ij=nai×bj c_n=\sum_{\begin{aligned}i\&j=0\\i|j=n\end{aligned}} a_i\times b_j

如果沒有 i&j=0i\&j=0 這個條件那麼就是個 FMTFMT 了,考慮怎麼處理這個東西。

不妨將每個數組增加一維,設 acnt(n),na_{cnt(n),n} 表示原來的 ana_n,其中 cnt(n)cnt(n) 表示集合 nn 內有多少個元素,在二進制下就是 11 的個數。

那麼 i&j=0i\&j=0 這個限制就變成了 cnt(i)+cnt(j)=cnt(n)cnt(i)+cnt(j)=cnt(n)

然後考慮將每個 aia_i 進行 FMTFMT 的正變換,然後像平常一樣 a,ba,b 對應位置乘起來得到 cc,不過要按照 cnt(i)+cnt(j)=cnt(n)cnt(i)+cnt(j)=cnt(n) 的規則來加。

最後把每個 cic_i 再逆變換回去,每個 ccnt(i),ic_{cnt(i),i} 就對應我們要的 cic_i

你谷模板題傳送門

這題有點神奇,用 freadfread 來優化會變慢……

代碼如下:

#include <cstdio>
#define mod 1000000009

int n,cnt[1<<20];
int A[21][1<<20],B[21][1<<20],C[21][1<<20];
int add(int x,int y){return x+y<mod?x+y:x+y-mod;}
void FWT(int *f,int type)
{
	for(int mid=1;mid<(1<<n);mid<<=1)
	for(int j=0;j<(1<<n);j+=(mid<<1))
	for(int i=j;i<j+mid;i++)f[i+mid]=add(f[i+mid],add(type*f[i],mod));
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<(1<<n);i++)cnt[i]=cnt[i-(i&(-i))]+1;
	for(int i=0;i<(1<<n);i++)scanf("%d",&A[cnt[i]][i]);
	for(int i=0;i<(1<<n);i++)scanf("%d",&B[cnt[i]][i]);
	for(int i=0;i<=n;i++)FWT(A[i],1),FWT(B[i],1);
	for(int i=0;i<=n;i++)for(int j=0;j<=i;j++)
	for(int k=0;k<(1<<n);k++)
	C[i][k]=add(C[i][k],1ll*A[j][k]*B[i-j][k]%mod);
	for(int i=0;i<=n;i++)FWT(C[i],-1);
	for(int i=0;i<(1<<n);i++)printf("%d ",C[cnt[i]][i]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章