【FWT】题目总结

3 篇文章 1 订阅

之前做过的 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(jk=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;
}

bzoj4589

博弈论+ 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

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值