[HDU6057] Kanade‘s convolution (FWT)

题面

出自HDU6057

给你两个数列 A [ 0... 2 m − 1 ] A[0...2^m-1] A[0...2m1] B [ 0... 2 m − 1 ] B[0...2^m-1] B[0...2m1]

请计算数列 C [ 0... 2 m − 1 ] C[0...2^m-1] C[0...2m1]:

C [ k ] = ∑ i    a n d    j = k A [ i    x o r    j ] ∗ B [ i    o r    j ] C[k]=\sum_{i\;and\;j=k}A[i\;xor\;j]*B[i\;or\;j] C[k]=iandj=kA[ixorj]B[iorj]

你只需要输出 ∑ i = 0 2 m − 1 C [ i ] ∗ 152 6 i  ⁣ ⁣ m o d    998244353 \sum_{i=0}^{2^m-1}C[i]*1526^i\!\!\mod 998244353 i=02m1C[i]1526imod998244353

m < = 19 m <= 19 m<=19

0 ≤ A [ i ] , B [ i ] < 998244353 0\leq A[i],B[i]<998244353 0A[i],B[i]<998244353

题解

公式中的条件让我们很想用快速沃尔什变换FWT,但是该公式和FWT基本式子大相径庭,无疑给我们造成了巨大的麻烦。

我们只有两条路可走,化式子,换元。

目前公式的形态想要化式子是毫无可能性的,我们只好先换元后再做打算。

我们知道,两个数异或、按位或、按位与三个运算是有等量关系的,即:

i    x o r    j = ( i    o r    j ) − ( i    a n d    j ) = ( i    o r    j )   x o r   ( i    a n d    j ) i\;xor\;j=(i\;or\;j)-(i\;and\;j)=(i\;or\;j)\,xor\,(i\;and\;j) ixorj=(iorj)(iandj)=(iorj)xor(iandj)

有了这个关系,我们就可以消去其中一个甚至两个运算,不妨设 x = i    x o r    j , y = i    o r    j x=i\;xor\;j,y=i\;or\;j x=ixorj,y=iorj,则

x = y − ( i    a n d    j ) = y    x o r   ( i    a n d    j ) ⇔ x    x o r    y = ( i    a n d    j ) x    x o r    y = k        ( x ⊆ y , 即 x    a n d    y = x ) x=y-(i\;and\;j)=y\;xor\,(i\;and\;j)\\ \Leftrightarrow x\;xor\;y=(i\;and\;j)\\ x\;xor\;y=k\;\;\;(x\subseteq y,即 x\;and\;y=x) x=y(iandj)=yxor(iandj)xxory=(iandj)xxory=k(xy,xandy=x)

化到这一步,不免心中大喜,想要直接代成 C [ k ] = ∑ x   x o r   y = k A [ x ] ∗ B [ y ] C[k]=\sum_{x\,xor\,y=k}A[x]*B[y] C[k]=xxory=kA[x]B[y],并用类似子集卷积的方式限制位数来完成那热血沸腾的最后一步。

若真是这么简单,可就大错特错。这里有个坑,虽然有样例等各种数据的鼎力相助,发现这个坑对于OIer来说并不难,但当我们发现它,却着实令人倒吸一口凉气。

我们发现二元组 ( x , y ) (x,y) (x,y) ( i , j ) (i,j) (i,j) 并不是一一对应!

没错,这有点令人吃惊,发现之初,甚至笔者有那么一瞬间考虑过重新化式子。这时候,冷静的思考就显得格外重要。重新化式子不是不可能,但无疑会令人士气大挫,想要再成功地化式子,除非是顶级高手,不然短时间内绝对办不到。沉吟片刻,我们自然而然地发现了其中的内在关系。

一对 ( x , y ) (x,y) (x,y) 对应的 ( i , j ) (i,j) (i,j) 不一定唯一,但它们的数量确是准确的 2 c o u n t ( x ) 2^{count(x)} 2count(x)

c o u n t ( x ) count(x) count(x) 作为 x 中 1 的个数的定义不必多言,但为什么呢?

从定义入手,确定了 x 和 y 后, i    a n d    j i\;and\;j iandj 就可以确定了,也就是说我们已经确定了 i i i j j j 中共有的 1 是哪些,这是唯一的,那么接下来 x ( i    x o r    j i\;xor\;j ixorj)则确定了 i i i j j j 中只有其一拥有的 1 的分布,这是不定的,既然这其中任意一个 1 既有可能属于 i i i ,又有可能属于 j j j ,那么总数不就自然是 2 c o u n t ( x ) 2^{count(x)} 2count(x) 了吗?

打通了阻碍后,接下来的化式子便水到渠成:
C [ k ] = ∑ x   x o r   y = k A [ x ] ∗ B [ y ] ∗ 2 c o u n t ( x )                      = ∑ x   x o r   y = k ( A [ x ] ∗ 2 c o u n t ( x ) ) ∗ B [ y ] ( x ⊆ y ) \begin{matrix}C[k]=\sum_{x\,xor\,y=k}A[x]*B[y]*2^{count(x)}\\ \;\;\;\;\;\;\;\;\;\;=\sum_{x\,xor\,y=k}(A[x]*2^{count(x)})*B[y]\\ (x\subseteq y)\end{matrix} C[k]=xxory=kA[x]B[y]2count(x)=xxory=k(A[x]2count(x))B[y](xy)
终于长舒一口气,我们可以用代码去演绎了……

(😅最近小说看的有点多,啰嗦了点,但思路应该比较清晰)

CODE

顺便把FWT全模板附在代码里了,可能与笔者自己的学习笔记有点出入,但无大碍。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN (1<<20|5)
#define LL long long
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
LL read() {
	LL f=1,x=0;char s = getchar();
	while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
	while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
	return f * x;
}
char readchar() {
	char s = getchar();while(s == ' ' || s == '\n') s = getchar();
	return s;
}
const int MOD = 998244353;
const int inv2 = 998244354/2;
int n,m,i,j,s,o,k;
//----------------------FWT start-------------------------
int qm(int a,int M) {return (a>M? a-M:a);}
void DWTOR(int *s,int n) {
	for(int k = 1;k < n;k <<= 1) {
		for(int j = 0;j < n;j += (k<<1)) {
			for(int i = j;i < j+k;i ++) {
				(s[i+k] += s[i]) %= MOD;
			}
		}
	}return ;
}
void IDWTOR(int *s,int n) {
	for(int k = n/2;k > 0;k >>= 1) {
		for(int j = 0;j < n;j += (k<<1)) {
			for(int i = j;i < j+k;i ++) {
				(s[i+k] += MOD-s[i]) %= MOD;
			}
		}
	}return ;
}

void DWTAND(int *s,int n) {
	for(int k = 1;k < n;k <<= 1) {
		for(int j = 0;j < n;j += (k<<1)) {
			for(int i = j;i < j+k;i ++) {
				(s[i] += s[i+k]) %= MOD;
			}
		}
	}return ;
}
void IDWTAND(int *s,int n) {
	for(int k = n/2;k > 0;k >>= 1) {
		for(int j = 0;j < n;j += (k<<1)) {
			for(int i = j;i < j+k;i ++) {
				(s[i] += MOD-s[i+k]) %= MOD;
			}
		}
	}return ;
}

void DWTXOR(int *s,int n) {
	for(int k = 1;k < n;k <<= 1) {
		for(int j = 0;j < n;j += (k<<1)) {
			for(int i = j;i < j+k;i ++) {
				int A = s[i],B = s[i+k];
				s[i] = qm((A + B) , MOD);
				s[i+k] = qm((A +MOD- B) , MOD);
			}
		}
	}return ;
}
void IDWTXOR(int *s,int n) {
	for(int k = n/2;k > 0;k >>= 1) {
		for(int j = 0;j < n;j += (k<<1)) {
			for(int i = j;i < j+k;i ++) {
				int A = s[i],B = s[i+k];
				s[i] = qm((A + B) , MOD) *1ll*inv2 % MOD;
				s[i+k] = qm((A +MOD- B) , MOD) *1ll*inv2 % MOD;
			}
		}
	}return ;
}
//----------------------FWT end---------------------------
int ct[MAXN],po[MAXN];
int A[21][MAXN],B[21][MAXN],C[21][MAXN];
int aa[MAXN],bb[MAXN],cc[MAXN];
int main() {
	n = read();m = (1<<n);
	po[0] = 1;
	for(int i = 1;i < m;i ++) {
		ct[i] = ct[i - lowbit(i)] + 1;
		po[i] = po[i - 1] * 1526ll % MOD;
	}
	for(int i = 0;i < m;i ++) A[ct[i]][i] = read()*(1ll<<ct[i]) % MOD;
	for(int i = 0;i < m;i ++) B[ct[i]][i] = read();
	for(int i = 0;i <= n;i ++) DWTXOR(A[i],m),DWTXOR(B[i],m);
	for(int i = 0;i <= n;i ++) {
		for(int j = i;j <= n;j ++) {
			for(int k = 0;k < m;k ++) {
				(C[i][k] += A[j-i][k] *1ll* B[j][k] % MOD) %= MOD;
			}
		}
		IDWTXOR(C[i],m);
	}
	int ans = 0;
	for(int i = 0;i < m;i ++) {
		(ans += C[ct[i]][i] *1ll* po[i] % MOD) %= MOD;
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-02-07 20:15  DD_XYX  阅读(25)  评论(0编辑  收藏  举报