LG P6570 [NOI Online #3 提高组]优秀子序列

Description

给定一个长度为 $n$ 的非负整数序列 $A=\{a_1,a_2,\cdots,a_n\}$,对于 $A$ 的一个子序列 $B=\{a_{b_1},a_{b_2},\cdots,a_{b_m}\}$($0\le m\le n$,$1\le b_1 < b_2 < \cdots < b_m\le n$,下同),称 $B$ 是 $A$ 的优秀子序列当且仅当,其任意两个不同元素的按位与结果均为 $0$,即:$\forall 1\le i < j\le m$,满足:$a_{b_i}~\mathrm{and}~a_{b_j}=0$,其中 $~\mathrm{and}~$ 是按位与运算。

对于子序列 $B=\{a_{b_1},a_{b_2},\cdots,a_{b_m}\}$,我们定义其价值为 $\varphi(1+\sum_{i=1}^m a_{b_i})$,其中 $\varphi(x)$ 表示小等于 $x$ 的正整数中与 $x$ 互质的数的个数。

现在请你求出 $A$ 的所有优秀子序列的价值之和,答案对 $10^9+7$ 取模。

Solution

如果将每个数视为$2$的幂次的集合,那么题目就是要求$\sum_{x \in S}x$,$S$是一个合法序列

因为$2$的幂次相加不会进位,上式就是$\cup_{x \in S}x$

设$dp_i$表示合法序列中所有元素并为$i$的方案数

那么题中所求即为

$$\sum dp_x \varphi(x+1)$$

$\varphi$可以一次线性筛求出,$dp$可以一次子集DP求出

最后加上空集

LG P6570 [NOI Online #3 提高组]优秀子序列
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long n,maxx,tot,m;
long long dp[400005]={1},phi[400005],buc[400005],prime[400005],a[1000005],ans=1;
bool vst[400005];
const long long mod=1e9+7;
inline long long read()
{
    long long f=1,w=0;
    char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
    return f*w;
}
void pre(long long N)
{
    phi[1]=1;
    for(long long i=2;i<=N;i++)
    {
        if(!vst[i]) prime[++tot]=i,phi[i]=i-1;
        for(long long j=1;j<=tot&&i*prime[j]<=N;j++)
        {
            vst[i*prime[j]]=true;
            if(!(i%prime[j]))
            {
                phi[i*prime[j]]=phi[i]*prime[j];break;
            }
            else phi[i*prime[j]]=phi[i]*phi[prime[j]];
        }
    }
}
int main()
{
    n=read();
    for(long long i=1;i<=n;i++) a[i]=read(),maxx=max(maxx,a[i]),buc[a[i]]++;
    while((1<<m)<=maxx) m++;
    pre(1<<m);
    for(long long i=1;i<=(1<<m);i++)
    {
        for(long long s=i;;s=(s-1)&i)
        {
            long long t=i^s;
            if(s<t) break;
            (dp[i]+=dp[t]*buc[s]%mod)%=mod;
        }
        (ans+=dp[i]*phi[i+1]%mod)%=mod;
    }
    for(long long i=1;i<=buc[0];i++) (ans*=2)%=mod;
    printf("%lld\n",ans);
    return 0;
}
优秀子序列

 

上一篇:信号处理--线性卷积与循环卷积


下一篇:动态规划之——最长公共子串和矩阵链乘法