子集卷积

还是之前那个题:
http://codeforces.com/contest/914/problem/G
那个题的复杂度其实可以长这样子:
O(n22n+n2n) O ( n 2 2 n + n 2 n )
只要用子集卷积就好了
子集卷积的定义大概长这样子:
CK=L2UR2U[LR=K][LR=]ALBR C K = ∑ L ∈ 2 U ∑ R ∈ 2 U [ L ∪ R = K ] [ L ∩ R = ∅ ] A L ∗ B R
其实也就是那个题的第一部分
我们怎么处理它呢?
我们可以先进行一步变形,改掉那个交集限制,变成这样子:
CK=L2UR2U[LR=K][|L|+|R|=|K|]ALBR C K = ∑ L ∈ 2 U ∑ R ∈ 2 U [ L ∪ R = K ] [ | L | + | R | = | K | ] A L ∗ B R
这样子就好看多了,因为把交集变成了加法
现在我们就可以再加一维表示集合大小,列个dp式子:
dpi,SSi d p i , S 代 表 集 合 为 S , 大 小 为 i 的 方 案 数
然后就可以dp了
这个东西是一个二维卷积的形式,而且两维互不相关,所以我们可以分开考虑(有相关定理)
首先我们先对每一个i进行FWT,先进行了集合并卷积,然后我们枚举集合大小进行另外一维卷积
然后最后再IFWT回来就好了,过程中要再开一个dp数组,记录答案,不然就是错的
其实主要是代码,自己看看,就是把 3n 3 n 暴力枚举改成了子集卷积

#include
#include
#include
#include
#include
#include
#define LL long long
using namespace std;
inline int read(){
    int x=0,f=1;char ch=' ';
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
const LL N=1<<17,mod=1e9+7,inv=5e8+4;

inline void FWT_AND(LL *a){
    for(int d=1;d1)
        for(int i=0;i1)
            for(int j=0;jinline void IFWT_AND(LL *a){
    for(int d=1;d1)
        for(int i=0;i1)
            for(int j=0;jinline void FWT_OR(LL *a){
    for(int d=1;d1)
        for(int i=0;i1)
            for(int j=0;jinline void IFWT_OR(LL *a){
    for(int d=1;d1)
        for(int i=0;i1)
            for(int j=0;jinline void FWT_XOR(LL *a){
    for(int d=1;d1)
        for(int i=0;i1)
            for(int j=0;jinline void IFWT_XOR(LL *a){
    for(int d=1;d1)
        for(int i=0;i1)
            for(int j=0;j20][N],dp2[20][N],t[N],size[N];
inline void solve(){
    for(int i=0;i<18;++i)FWT_OR(dp[i]);
    for(int i=0;i<18;++i)
        for(int j=0;j<=i;++j)
            for(int k=0;kfor(int i=0;i<18;++i)IFWT_OR(dp2[i]);
    for(int i=0;iint main(){
    fib[1]=1;
    for(int i=2;i1]+fib[i-2])%mod;
    for(int s=0;sfor(int i=0;i<18;++i)
            if((s>>i)&1)
                ++size[s];

    n=read();
    for(int i=1;i<=n;++i){
        int x=read();
        a[x]++;
    }
    for(int i=0;i0;
        B[i]=a[i];
        C[i]=a[i];
        dp[size[i]][i]=a[i];
    }
    solve();
    FWT_XOR(C);
    for(int i=0;ifor(int i=0;ifor(int i=0;ifor(int i=0;ifor(int i=0;i0;
    for(int i=0;i<17;++i)ans=(ans+A[1<printf("%I64d",ans);
    return 0;
}

你可能感兴趣的:(子集卷积谁知道题呀)