版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/81981599
Description
给定 n 个正整数序列
,每个序列长度为
。
选择至少
个序列,在每个被选择的序列中选择一个元素,求出所有被选择的元素的
。
求所有方案的结果之和,答案对 1e9+7 取模。两种方案不同,当且仅当存在至少一个元素,在一种方案中被选择,在另一种中没有。
Input
第一行,两个正整数
。
接下来
行,每行
个正整数,第i 行代表序列 。
Output
第一行,一个整数,代表答案对
取模的结果。
分析:
显然可以反演。
记
为第
个数列,元素是
的倍数的个数
然后换元,设 ,
后面那个就是 ,
预处理可以 ,后面计算是 的。
考场上并没有注意这个是 ,直接当做一般积性函数做了。
代码:
#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);
}