jzoj 5843.【省选模拟2018.8.23】b 莫比乌斯反演

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/81981599

Description
给定 n 个正整数序列 a 1 , a 2 , a 3 , . . . . , a n ,每个序列长度为 m
选择至少 1 个序列,在每个被选择的序列中选择一个元素,求出所有被选择的元素的 g c d
求所有方案的结果之和,答案对 1e9+7 取模。两种方案不同,当且仅当存在至少一个元素,在一种方案中被选择,在另一种中没有。

Input
第一行,两个正整数 n m
接下来 n 行,每行 m 个正整数,第i 行代表序列 。

Output
第一行,一个整数,代表答案对 10 9 + 7 取模的结果。

n <= 20 , m <= 10 5 , a i , j <= 10 5

分析:
显然可以反演。
s u m [ i ] [ j ] 为第 i 个数列,元素是 j 的倍数的个数

a n s = i = 1 m a x a i d = 1 i / d μ ( d ) ( k = 1 n ( s u m [ k ] [ i d ] + 1 ) 1 )

然后换元,设 T = i d
a n s = T = 1 m a x a ( k = 1 n ( s u m [ k ] [ T ] + 1 ) 1 ) i | T μ ( i ) ( i / d )

后面那个就是 ϕ
a n s = T = 1 m a x a ( k = 1 n ( s u m [ k ] [ T ] + 1 ) 1 ) ϕ ( T )

预处理可以 O ( n m l o g m ) ,后面计算是 O ( m ) 的。
考场上并没有注意这个是 ϕ ,直接当做一般积性函数做了。

代码:

#include <iostream>
#include <cmath>
#include <cstdio>
#define LL long long

const int maxn=22;
const int maxm=1e5+7;
const int maxa=1e5;
const LL mod=1e9+7;

using namespace std;

LL n,m,x,cnt;
LL prime[maxm],not_prime[maxm],low[maxm];
LL ans,sum[maxm],a[maxn][maxm],f[maxm];

void getmul(LL n)
{
    f[1]=1;
    for (LL i=2;i<=n;i++)
    {
        if (!not_prime[i])
        {
            prime[++cnt]=i;
            f[i]=i-1;
            low[i]=i;
        }
        for (LL j=1;j<=cnt;j++)
        {
            if (i*prime[j]>n) break;
            not_prime[i*prime[j]]=1;
            if (low[i]%prime[j]==0) low[i*prime[j]]=low[i]*prime[j];
                               else low[i*prime[j]]=prime[j];
            if (i*prime[j]==low[i*prime[j]])
            {
                f[i*prime[j]]=(i*prime[j]-i)%mod;
            }
            else
            {
                LL x=low[i*prime[j]],y=i*prime[j]/x;
                f[i*prime[j]]=(f[x]*f[y])%mod;
            }
            if (i%prime[j]==0) break;
        }
    }
}

int main()
{
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    for (LL i=1;i<=n;i++)
    {
        for (LL j=1;j<=m;j++)
        {
            scanf("%lld",&x);
            a[i][x]++;
        }
    }
    for (LL i=1;i<=maxa;i++) sum[i]=1;
    for (LL i=1;i<=n;i++)
    {
        for (LL j=1;j<=maxa;j++)
        {           
            for (LL k=j+j;k<=maxa;k+=j)
            {
                a[i][j]=(a[i][j]+a[i][k])%mod;
            }           
            sum[j]=(sum[j]*(a[i][j]+1))%mod;
        }
    }   
    getmul(maxa);   
    for (LL i=1;i<=maxa;i++)
    {
        ans=(ans+(sum[i]+mod-1)%mod*f[i]%mod)%mod;
    }
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81981599