【HDU6057】Kanade's Convolution-FWT
测试地址:Kanade’s Convolution
题目大意:给定两个长为的向量,计算向量,其中:
做法:本题需要用到FWT。
看到这种类似卷积的东西,又是关于位运算,自然想到FWT。但是这个式子看上去实在复杂,三种位运算都有,貌似不可算,所以我们先化一下式子:
首先我们根据位运算的定义,有:。于是我们把枚举改成枚举,那么条件就变成了,根据之间的特殊关系,减号可以换成。在这种情况下,需要有一个约束条件使得存在一些与一对对应的,这个约束条件为。又根据一些二进制位的关系可以得出,一对这样的所对应的对数为,其中指的二进制位数。这样我们就把式子化成了下面的形式:
这时候这个式子已经很有卷积的感觉了,关键是方括号内的部分怎么处理。这时候我们需要把条件换成另一个等价的条件。当时,和是等价的,证明还是从间的特殊关系着手。于是式子变成:
令向量(实际上就是把为的所有单独抽出来),再令,有:
根据FWT得出的点值表示的性质,这些东西可以通过FWT后,直接对位相加、乘来计算,最后再把这一大堆进行逆FWT即可。
注意到,于是我们只需按照上面的方法计算出即可解决此题,时间复杂度为。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const ll inv=(mod+1)>>1;
int m,n,bit[600010];
ll a[600010],b[600010];
ll A[20][600010]={0},B[20][600010]={0},C[20][600010]={0};
void FWT(ll *a,int n,int type)
{
for(int mid=1;mid<n;mid<<=1)
for(int l=0;l<n;l+=(mid<<1))
for(int k=0;k<mid;k++)
{
ll x=a[l+k],y=a[l+mid+k];
a[l+k]=(x+y)%mod;
a[l+mid+k]=(x-y+mod)%mod;
if (type==-1)
{
a[l+k]=a[l+k]*inv%mod;
a[l+mid+k]=a[l+mid+k]*inv%mod;
}
}
}
int main()
{
scanf("%d",&m);
n=(1<<m);
for(int i=0;i<n;i++)
scanf("%lld",&a[i]);
for(int i=0;i<n;i++)
scanf("%lld",&b[i]);
for(int i=0;i<n;i++)
{
bit[i]=0;
int x=i;
while(x)
{
if (x&1) bit[i]++;
x>>=1;
}
}
for(int i=0;i<n;i++)
{
A[bit[i]][i]=a[i]*(1ll<<bit[i])%mod;
B[bit[i]][i]=b[i];
}
for(int i=0;i<=m;i++)
FWT(A[i],n,1),FWT(B[i],n,1);
for(int i=0;i<=m;i++)
for(int j=0;j+i<=m;j++)
{
int k=j+i;
for(int p=0;p<n;p++)
C[i][p]=(C[i][p]+A[j][p]*B[k][p])%mod;
}
for(int i=0;i<=m;i++)
FWT(C[i],n,-1);
ll now=1,ans=0;
for(int i=0;i<n;i++)
{
ans=(ans+C[bit[i]][i]*now)%mod;
now=now*1526ll%mod;
}
printf("%lld\n",ans);
return 0;
}