题目链接:https://www.luogu.com.cn/problem/P4717
博客园食用链接:https://www.cnblogs.com/lonely-wind-/p/13472894.html
题目描述
给定长度为 两个序列 ,设 ,分别当 是or,and,xor时求出
输入格式
第一行一个数n,第二行
个数
,第三行
个数
输出格式
三行每行
个数,分别代表
是or,and,xor时
的值
输入
2
2 4 6 8
1 3 5 7
输出
2 22 46 250
88 64 112 56
100 92 68 60
说明/提示
。
emmm,真的对于这些构造怎么得来的,不用太过深究,and,or运算还好,但xor的构造。。。我真的看了很久也不太明白怎么构造出来的,为什么是这样的。。。只能说是它是经得起验证的QAQ
对于卷积或:
我们知道或的性质有:
且
,则
,那么我们可以定义一个正变换:
,那么则有如下证明:
也就是说确实是OK的。那么我们就可以通过一次
的正变换将
转化为
,之后我们将其对应的数乘起来就会得到
,那么我们就可以再通过一次逆变换将
转换为
,也就是如下片段:
int len=1<<n,typefornt=1,typeback=-1;
FWT::FWTOR(a,len,typefornt); FWT::FWTOR(b,len,typefornt);
for (int i=0; i<len; i++)
c[i]=1LL*a[i]*b[i]%mod;
FWT::FWTOR(c,len,typeback);
for (int i=0; i<len; i++)
printf("%d ",c[i]);
printf("\n");
实际上这个步骤是通用的,都是一个模样。只不过求 的过程就会不太好写,对于or我们可以得到如下的片段:
void FWTOR(int *ary,const int &len,const int &typ) {
for(int L=2,T=1; L<=len; L<<=1,T<<=1)
for(int i=0; i<len; i+=L)
for(int j=i; j<i+T; j++)
if(typ==1) ary[j+T]=FAdd(ary[j+T],ary[j]);
else ary[j+T]=FSub(ary[j+T],ary[j]);
}
对于卷积与运算,其性质和或的相似:
且
则有
,那么我们就可以对构造
则有如下代码:
void FWTAND(int *ary,const int &len,const int &typ) {
for(int L=2,T=1; L<=len; L<<=1,T<<=1)
for(int i=0; i<len; i+=L)
for(int j=i; j<i+T; j++)
if(typ==1) ary[j]=FAdd(ary[j],ary[j+T]);
else ary[j]=FSub(ary[j],ary[j+T]);
}
接下来就是神仙xor了。。。。我只能orz
先定义
,其中
表示的是二进制下1的个数,如果它是奇数的话就是1,否则就是0。则可得到性质
它的构造就是
。进行验证的话就是
emmmm。。。。
验证的话没什么难度,但是构造就有点迷了QAQ,算了,用着用着就应该熟悉了。
xor卷积代码:
void FWTXOR(int *ary,const int &len,const int &typ) {
for(int L=2,T=1; L<=len; L<<=1,T<<=1)
for(int i=0; i<len; i+=L)
for(int j=i; j<i+T; j++) {
int Aj=ary[j];
ary[j]=FAdd(Aj,ary[j+T]);
ary[j+T]=FSub(Aj,ary[j+T]);
if(typ==-1)
ary[j]=FMul(ary[j],INV2),ary[j+T]=FMul(ary[j+T],INV2);
}
}
以下是AC代码:
#include <bits/stdc++.h>
using namespace std;
const int mac=(1<<17)+10;
const int mod=998244353;
int a[mac],b[mac],c[mac];
int aa[mac],bb[mac];
namespace FWT{
const int MOD=998244353;
inline int FAdd(const int &a,const int &b){return a+b>=MOD? a+b-MOD:a+b;}
inline int FSub(const int &a,const int &b){return a-b<0? a-b+MOD:a-b;}
inline int FMul(const int &a,const int &b){return 1ll*a*b%MOD;}
inline int FPow(int a,int b){int ret=1;while(b){if(b&1)ret=FMul(ret,a);a=FMul(a,a);b>>=1;}return ret;}
const int INV2=FPow(2,MOD-2);
void FWTOR(int *ary,const int &len,const int &typ){
for(int L=2,T=1;L<=len;L<<=1,T<<=1)
for(int i=0;i<len;i+=L)
for(int j=i;j<i+T;j++)
if(typ==1) ary[j+T]=FAdd(ary[j+T],ary[j]);
else ary[j+T]=FSub(ary[j+T],ary[j]);
}
void FWTAND(int *ary,const int &len,const int &typ){
for(int L=2,T=1;L<=len;L<<=1,T<<=1)
for(int i=0;i<len;i+=L)
for(int j=i;j<i+T;j++)
if(typ==1) ary[j]=FAdd(ary[j],ary[j+T]);
else ary[j]=FSub(ary[j],ary[j+T]);
}
void FWTXOR(int *ary,const int &len,const int &typ){
for(int L=2,T=1;L<=len;L<<=1,T<<=1)
for(int i=0;i<len;i+=L)
for(int j=i;j<i+T;j++){
int Aj=ary[j];
ary[j]=FAdd(Aj,ary[j+T]);
ary[j+T]=FSub(Aj,ary[j+T]);
if(typ==-1)
ary[j]=FMul(ary[j],INV2),ary[j+T]=FMul(ary[j+T],INV2);
}
}
}
int main(int argc, char const *argv[])
{
int n;
scanf ("%d",&n);
for (int i=0; i<1<<n; i++)
scanf ("%d",&a[i]);
for (int i=0; i<1<<n; i++)
scanf ("%d",&b[i]);
for (int i=0; i<1<<n; i++)
aa[i]=a[i],bb[i]=b[i];
int len=1<<n,typefornt=1,typeback=-1;
FWT::FWTOR(a,len,typefornt); FWT::FWTOR(b,len,typefornt);
for (int i=0; i<len; i++)
c[i]=1LL*a[i]*b[i]%mod;
FWT::FWTOR(c,len,typeback);
for (int i=0; i<len; i++)
printf("%d ",c[i]); printf("\n");
for (int i=0; i<1<<n; i++) a[i]=aa[i],b[i]=bb[i];
FWT::FWTAND(a,len,typefornt); FWT::FWTAND(b,len,typefornt);
for (int i=0; i<len; i++)
c[i]=1LL*a[i]*b[i]%mod;
FWT::FWTAND(c,len,typeback);
for (int i=0; i<len; i++)
printf("%d ",c[i]); printf("\n");
for (int i=0; i<1<<n; i++) a[i]=aa[i],b[i]=bb[i];
FWT::FWTXOR(a,len,typefornt); FWT::FWTXOR(b,len,typefornt);
for (int i=0; i<len; i++)
c[i]=1LL*a[i]*b[i]%mod;
FWT::FWTXOR(c,len,typeback);
for (int i=0; i<len; i++)
printf("%d ",c[i]); printf("\n");
return 0;
}