CodeChef: Covering Sets —— SOSDP+容斥

This way

题意:

现在有 2 n 2^{n} 2n种状态,从0开始,定义三元组 { a , b , c } \{a,b,c\} {a,b,c}包括 d d d当且仅当 d d d a ∣ b ∣ c a|b|c abc的子集。
a属于f()函数,b属于g()函数,c属于r()函数。
现在给你所有的f(x),g(x),r(x)的返回值 0 &lt; = x &lt; 2 n 0&lt;=x&lt;2^n 0<=x<2n,定义r(d)是所有 f ( a ) ∗ g ( b ) ∗ r ( c ) f(a)*g(b)*r(c) f(a)g(b)r(c)的和当且仅当d是a,b,c三元组的子集。
问你 ∑ i = 0 2 n − 1 r ( i ) \sum^{2^n-1}_{i=0}r(i) i=02n1r(i)的值

题解:

有点难度的SOSDP了,这道题不能直接将答案存在DP数组里,而是要知道对于 a ∣ b ∣ c = x a|b|c=x abc=x这种状态,总共有 2 d ( x ) 2^{d(x)} 2d(x)个r会受到它影响 d ( x ) d(x) d(x)表示x状态下二进制位上1的个数(这个理解一下就懂了)。那么我们用3个dp求出f,g,r三种函数的SOSDP,再将其相乘变成对应的乘积,对于乘积我们再做一次高维前缀和,这样 m u l [ x ] mul[x] mul[x]就表示 a ∣ b ∣ c a|b|c abc是x的所有子集的答案的和,所以我们要剪掉所有 m u l [ x ] mul[x] mul[x]的子状态的和,由与它是用高维前缀和推过来的,所以容斥只需要一次即可,这样会将所有多余情况剪掉。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1<<20;
const ll mod=1e9+7;
ll f[N],g[N],r[N],mul[N],p[21];
int main()
{
    p[0]=1;
    for(int i=1;i<=20;i++)
        p[i]=p[i-1]*2;
    int n;
    scanf("%d",&n);
    for(int i=0;i<1<<n;i++)
        scanf("%lld",&f[i]);
    for(int i=0;i<1<<n;i++)
        scanf("%lld",&g[i]);
    for(int i=0;i<1<<n;i++)
        scanf("%lld",&r[i]);
    ll ans=0;
    for(int i=0;i<20;i++)
        for(int j=0;j<N;j++)
            if(j&(1<<i))
            {
                f[j]=(f[j]+f[j-(1<<i)])%mod;
                g[j]=(g[j]+g[j-(1<<i)])%mod;
                r[j]=(r[j]+r[j-(1<<i)])%mod;
            }
    for(int i=0;i<N;i++)
        mul[i]=f[i]*g[i]%mod*r[i]%mod;
    for(int i=0;i<20;i++)
        for(int j=0;j<N;j++)
            if(j&(1<<i))
                mul[j]=(mul[j]-mul[j-(1<<i)]+mod)%mod;
    for(int i=0;i<N;i++)
    {
        int sum=0;
        for(int j=0;j<20;j++)
            if(i&(1<<j))
                sum++;
        ans=(ans+mul[i]*p[sum])%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值