4770 优秀子序列

4770 优秀子序列_第1张图片

简洁题意
要求从 A 选出子集 B 使得 B 中没有两个数按位与等于 0。每个子集 B 的价值是集合内所有数的和加一的 φ 值。求所有子集 B 的价值之和。

分析
可以思考,因为要知道所有的集合 B 不容易,所以考虑计算价值相同的 B 的个数。

设 f[i] 表示所有值为 i 的子集 B 的数量,我们算出 f[i] 后乘上 φ[i+1] 就是这个方案的价值和,把每个价值和加在一起就是答案。

考虑怎么计算这个 f ,我们可以把 f 拆成两部分,然后一部分直接用 f[j] ,另一部分用集合 A 的一个数来补上就行。因为题目要 B 中所有数的按位与均等于 0 ,所以不会出现有两个数的某一位都是 1 的情况。所以我们在拆 i 的时候可以把 i 中为 1 的位分成两半来计算。为了方便处理,我们把 num[x] 表示 a[j] 中值为 x 的个数, s 表示 i 分出来的一个数, t 为剩余的部分,为了保证不重复计算,要求 s≥ts ,根据乘法原理,则有状态转移方程  。

啊啊啊!没LaTeX好难受!!!!!
可是这样就有一个问题:这些数的和的上限很大,内存装不下啊!不用担心,因为我们选出来的数不会有两位都是1的情况,所以选出来的和自然小于 ,以  为上限即可。

但是这样就会出现一个情况:a[i]=0 算漏了!其实 a[i] 等于 0 时用不用都没关系,根据乘法原理,所以答案要乘上 2^{num0} 。

#include
using namespace std;
const int NN=300000,P=1e9+7;
int num[NN],phi[NN],f[NN];
bool vis[NN];
int main()
{
    int n,maxx=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        num[x]++;
        maxx=max(maxx,x);
    }
    int m=0;
    while((1<

你可能感兴趣的:(算法,c++)