之前做过的 F W T FWT FWT的题好多都没写博客一块写一下
不会
F
W
T
FWT
FWT的话请右转讲解部分
板子上面也有,下面是一些不那么裸的题(其实还是很裸的)
CF662C 我做的第一道 F W T FWT FWT
把矩阵压缩成
m
m
m个二进制数,可以枚举
2
n
2^n
2n表示要反转那些行,发现反转其实就是把那些二进制数异或上当前反转集合
i
i
i,假设
d
(
x
)
d(x)
d(x)表示二进制数
x
x
x中
m
i
n
(
c
n
t
1
,
c
n
t
0
)
min(cnt_1,cnt_0)
min(cnt1,cnt0),因为可以通过反转竖列来达到取
m
i
n
min
min的效果
所求就变成了
m
i
n
i
=
0
2
n
(
∑
j
=
1
m
d
(
a
[
j
]
⊕
i
)
)
min_{i=0}^{2^n}(\sum_{j=1}^m d(a[j]\oplus i))
mini=02n(∑j=1md(a[j]⊕i))
看到了异或,就可以用
F
W
T
FWT
FWT来优化,变形一下式子
m
i
n
i
=
0
2
n
(
∑
j
⊕
k
=
i
d
(
k
)
×
b
(
j
)
)
min_{i=0}^{2^n}(\sum_{j\oplus k=i}d(k)\times b(j))
mini=02n(∑j⊕k=id(k)×b(j)),
b
[
i
]
b[i]
b[i]就表示原序列中
i
i
i有几个
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxm 200005
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
int n,m,str[maxm];
int a[(1<<22)+5],b[(1<<22)+5],c[(1<<22)+5],limit=1,l,ed,ans;
char s[maxm];
inline int min(int x,int y){return x<y?x:y;}
inline void FWT(int *F,int type){
for(int mid=1;mid<limit;mid<<=1)
for(int r=mid<<1,j=0;j<limit;j+=r)
for(int k=0;k<mid;k++){
int x=F[j+k],y=F[j+mid+k];
F[j+k]=x+y,F[j+mid+k]=x-y;
if(type==-1) F[j+k]>>=1,F[j+mid+k]>>=1;
}
}
signed main(){
scanf("%d%d",&n,&m); ed=1<<n;
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++)
if(s[j]=='1') str[j]|=(1<<(i-1));
}
for(int i=0;i<ed;i++) b[i]=__builtin_popcount(i),b[i]=min(b[i],n-b[i]);
for(int i=1;i<=m;i++) a[str[i]]++;
limit=ed; l=n;
FWT(a,1); FWT(b,1);
for(int i=0;i<limit;i++) a[i]=a[i]*b[i];
FWT(a,-1); ans=inf;
for(int i=0;i<ed;i++) ans=min(ans,a[i]);
printf("%lld\n",ans);
return 0;
}
CF453D
超裸的题,会
F
W
T
FWT
FWT的都能想出来
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 25
#define int long long
using namespace std;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
const int N=(1<<20)+5;
int m,mod,n,a[N],b[N],str[maxn],t,inv2;
inline int mul(int x,int y){
// int ret=0;
// while(k){
// if(k&1) (ret+=x)%=mod;
// (x+=x)%=mod; k>>=1;
// } return ret%mod; fast_tle?
long long tmp=(x*y-(long long)((long double)x/mod*y+1e-8)*mod);
return tmp<0?tmp+mod:tmp;
}
inline int qpow(int x,int k){
int ret=1;
while(k){
if(k&1) ret=mul(ret,x);
x=mul(x,x); k>>=1;
} return ret%mod;
}
inline int calc(int x){
int ret=0;
while(x){
if(x&1) ++ret; x>>=1;
} return ret;
}
inline void FWT(int *F,int type){
for(int mid=1;mid<n;mid<<=1)
for(int r=mid<<1,j=0;j<n;j+=r)
for(int k=0;k<mid;k++){
int x=F[j+k],y=F[j+mid+k];
F[j+k]=(x+y)%mod,F[j+mid+k]=(x-y+mod)%mod;
if(type==-1)
F[j+k]=F[j+k]>>1,F[j+mid+k]=F[j+mid+k]>>1;
}
}
signed main(){
m=rd(); t=rd(); mod=rd(); n=1<<m; mod*=n;
for(int i=0;i<n;i++) a[i]=rd()%mod;
for(int i=0;i<=m;i++) str[i]=rd()%mod;
for(int i=0;i<n;i++) b[i]=str[calc(i)];
FWT(a,1); FWT(b,1);
for(int i=0;i<n;i++){
b[i]=qpow(b[i],t);
a[i]=mul(a[i],b[i]);
}
FWT(a,-1); mod/=n;
for(int i=0;i<n;i++) printf("%I64d\n",a[i]%mod);
return 0;
}
博弈论+
F
W
T
FWT
FWT,所有堆的石子数的异或和为
0
0
0的话先手必胜
然后
n
n
n堆的话快速幂就好了,之前预处理一下质数。
可以转成点值以后直接快速幂,再转系数
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 200005
#define LL long long
using namespace std;
const int mod=1e9+7;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
int n,m,cnt,pri[maxn],limit=1;
LL a[maxn],b[maxn],inv2;
bool vis[maxn];
inline void get_pri(){
vis[1]=1;
for(int i=2;i<=50000;i++){
if(!vis[i]) pri[++cnt]=i;
for(int j=1;j<=cnt && i*pri[j]<=50000;j++){
vis[i*pri[j]]=1;
if(i%pri[j]==0) break;
}
}
}
inline LL qpow(LL x,int k){
LL ret=1;
while(k){
if(k&1) (ret*=x)%=mod;
(x*=x)%=mod; k>>=1;
} return ret;
}
inline void FWT(LL *F,int type){
for(int mid=1;mid<limit;mid<<=1)
for(int r=mid<<1,j=0;j<limit;j+=r)
for(int k=0;k<mid;k++){
LL x=F[j+k],y=F[j+mid+k];
F[j+k]=(x+y)%mod; F[j+mid+k]=(x-y+mod)%mod;
if(type==-1) (F[j+k]*=inv2)%=mod,(F[j+mid+k]*=inv2)%=mod;
}
}
int main(){
get_pri(); inv2=qpow(2,mod-2);
while(scanf("%d%d",&n,&m)!=EOF){
memset(b,0,sizeof b); memset(a,0,sizeof a);
for(int i=2;i<=m;i++)
if(!vis[i]) b[i]=1;
a[0]=1; limit=1;
while(limit<=m) limit<<=1;
FWT(a,1); FWT(b,1);
for(int i=0;i<limit;i++) a[i]=a[i]*qpow(b[i],n)%mod;
FWT(a,-1);
printf("%lld\n",a[0]);
}
return 0;
}
HDU5909
树形
d
p
+
F
W
T
dp+FWT
dp+FWT