Jzoj P5843 b
Jzoj P5843 b
考场没有做出来的题QwQ
看了题解加同机房大佬讲解后终于懂力~
考点:
容斥
莫比乌斯反演也可以~(虽然我不会)
题面:
选择至少 1 个序列,在每个被选择的序列中选择一个元素,求出所有被选择的元素的 gcd。两种方案不同,当且仅当存在至少一个元素,在一种方案中被选择,在另一种中没有。
注意即使两个序列中有一个相同的数num,那么num从第一个序列中被选择和从第二个序列中被选择依然被看成两个不同的方案。
思路:
最终考虑的是所有方案的gcd的和。
可以转化思路求每个gcd以及其出现的方案数cnt。
那么最终的和就是\(\sum gcd_i*cnt_i\),也就是每个gcd乘以其出现的方案数。
对于gcd,可以从\(1\)到maxnum枚举。maxnum为所有序列中最大的数。
对于每个gcd出现的次数,可以考虑容斥出来。
首先求出一个数组\(f[]\)
\(f[i]\)表示gcd为\(i\)以及\(i\)的倍数的方案数
int getf(int x,int v)//在第i个序列中为v的倍数的数的个数
{
int res=0;
for(int i=v;i<=maxnum;i+=v)
res=+=a[x][i];
return res+1;//也可以不选当前这一序列
}
for(int i=1;i<=maxum;i++){
f[i]=1;
for(int j=1;j<=n;j++)
f[i]=f[i]*getd(j,i)%mod;
f[i]=(f[i]-1+mod)%mod;//要去掉一种全空的方案
}
//在main函数里
然后求出一个数组\(g[]\)
\(g[i]\)表示gcd为\(i\)的方案数
gcd为\(i\)的方案数即为gcd为\(i\)以及\(i\)的倍数的方案数减去gcd为\(i\)的方案数。
即\(g[i]=f[i]-\sum_{i|d}g[d]\)
LL getg(int v)
{
LL res=0;
for(int i=2*v;i<=maxnum;i+=v)
res=(res+g[i])%mod;
return res;
}
for(int i=maxnum;i;i--)
g[i]=f[i]-cnt(i);
//在main函数里
因为要用\(f[i]\)减去gcd为\(i\)的倍数的方案数,所以在求\(g[i]\)之前应该求出所有\(g[d]\),其中\(i|d\),所以需要倒序枚举。
最后累计下\(i*g[i]\)就求出\(ans\)啦~
code
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long LL;
int const maxn=100101;
int const mod=1000000007;
int n,m;
int a[33][maxn],maxnum;
LL f[maxn],g[maxn],ans;
LL getf(int x,int v)
{
int res=0;
for(int i=v;i<=maxnum;i+=v)
res+=a[x][i];
return res+1;
}
LL getg(int v)
{
LL res=0;
for(int i=2*v;i<=maxnum;i+=v)
res=(res+g[i])%mod;
return res;
}
int main( )
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int num;scanf("%d",&num);
a[i][num]++;
maxnum=max(maxnum,num);
}
}
for(int i=1;i<=maxnum;i++){
f[i]=1;
for(int j=1;j<=n;j++)
f[i]=1ll*f[i]*getf(j,i)%mod;
f[i]=1ll*(f[i]-1+mod)%mod;
}
for(int i=maxnum;i>=1;i--)
g[i]=f[i]-getg(i);
for(int i=1;i<=maxnum;i++)
ans=(ans+(1ll*i*g[i]%mod))%mod;
printf("%lld",ans);
}

浙公网安备 33010602011771号