【知识点】子集卷积

简介:

就是FWT加了一维个数。

 

例题:

给定长度为$2^n$的多项式a,b,求一个多项式c,使得$c_k = sum limits_{i& j=0,i|j=k}{a_i b_j}$。

$nleq 20$。

 

题解:

普通的FWT能够解决$c_k = sum limits_{i|j=k}{a_i b_j}$的形式,考虑多的这个$i& j=0$的限制怎么办。

我们给多项式$f_j$增加一维变成$f_{i,j}$,如果状态j中1的个数为i($siz_j =i$)则$f_{i,j}=a_{j}$,否则$f_{i,j}=0$。

然后对于每个$f_i$所代表的多项式做一个子集和(正变换)。

此时$f_{i,S}$代表S的子集中满足$siz_k =i$的子集k的系数之和。(类似地,令$g_{i,S}$代表b)

现在就可以进行卷积了,令$h_i = sum limits_{j=0}^{i}{f_j * g_{i-j} }$。

此时$h_{i,S}=sum limits_{k|jsubseteq S,siz_k + siz_j = i}{a_k b_j}$。

我们再对每个$h_{i}$做一个子集差(逆变换),此时$h_{i,S}=sum limits_{k|j = S,siz_k + siz_j = i}{a_k b_j}$。

那么第S项(状态S)的答案就是$c_{S}=h_{|S|,S}$。

复杂度$O(n^{2}2^{n})$。(为啥洛谷的模板题卡常卡空间啊,我吐了)

 

代码:

【知识点】子集卷积

#include<bits/stdc++.h>
#define maxn 1048580
#define maxm 21
#define inf 0x7fffffff
#define mod 1000000009
#define ll long long
#define rint register int
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
ll siz[maxn];
struct poly{
    vector<int> a; ll n;
    inline void clear(){n=0;}
    inline void fwt(ll op){
        for(rint l=2;l<=n;l<<=1)
            for(rint i=0;i<n;i+=l)
                for(rint j=i;j<i+l/2;j++)
                    a[j+l/2]=(a[j+l/2]+op*a[j]+mod)%mod;
    }
    poly operator+(const poly b)const{
        poly res; res.n=max(n,b.n);
        for(rint i=0;i<res.n;i++){
            if(i>=min(n,b.n)) res.a.push_back((n<b.n)?b.a[i]:a[i]);    
            else res.a.push_back((a[i]+b.a[i])%mod);
        }
        return res;
    }
    poly operator*(const poly b)const{
        poly res; res.n=max(n,b.n);
        for(rint i=0;i<res.n;i++){
            if(i>=min(n,b.n)) res.a.push_back(0);    
            else res.a.push_back(((ll)a[i]*(ll)b.a[i])%mod);
        }
        return res;
    }
};

inline ll read(){
    ll x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline poly calc(poly A,ll m){
    poly T; T.n=A.n;
    for(rint i=0;i<A.n;i++) 
        T.a.push_back((siz[i]==m)?A.a[i]:0);
    T.fwt(1); return T;
}

inline void print(poly A){
    printf("%d:",A.n);
    for(int i=0;i<A.n;i++) printf(" %d",A.a[i]);
    printf("n");
}

int main(){
    ll n=read(),m=1<<n; poly A,B; 
    A.clear(),B.clear(),A.n=B.n=m;
    for(rint i=0;i<m;i++) A.a.push_back(read());
    for(rint i=0;i<m;i++) B.a.push_back(read());
    for(rint i=0;i<m;i++)
        for(rint j=0;j<n;j++)
            siz[i]+=((i>>j)&1);
    poly RA[maxm],RB[maxm],R[maxm];
    for(ll i=0;i<=n;i++)
        RA[i]=calc(A,i),RB[i]=calc(B,i);
    for(rint i=0;i<=n;i++){
        R[i].n=0;
        for(rint j=0;j<=i;j++)
            R[i]=(R[i]+(RA[j]*RB[i-j]));
        R[i].fwt(-1);
    }
    for(rint i=0;i<m;i++)
        printf("%lld ",R[siz[i]].a[i]);
    printf("n");
    return 0;
}

子集卷积

 

原文链接: https://www.cnblogs.com/YSFAC/p/13265021.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    【知识点】子集卷积

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/363955

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 下午3:18
下一篇 2023年3月2日 下午3:18

相关推荐