NOIP模拟38:b
这是T2。
一个容斥(其实也可以欧拉反演做,但是我不会)。
首先开一个桶,记录第i行的j有多少个。
然后枚举1~\(maxn\),枚举他的值域内的倍数,记录倍数在第i行有多少个,将个数记录在\(c[i][j]\)里
然后计算对每个j\(\prod_{i=1}^{n}(c[i][j]+1)\)
这个式子的意义是他的倍数的选法方案数,其中加一表示这一行不选的情况,展开后有一个1的常数项表示所有行都不选,是非法的,要减掉。
所以最终的方案数是他减一。
这是他倍数的选择方案,其中包括了以他为\(gcd\)的方案以及以他的倍数为\(gcd\)的方案。
所以要计算出以他的倍数为\(gcd\)的方案后在减掉才是以j为\(gcd\)的方案数,这一过程是逆推,对于一些i来说他的所有倍数(1倍除外)都不在值域内,他们的连乘结果就是以他们为\(gcd\)的方案数,可以以他们为起点逆推。
Code
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define rr register
typedef long long ll;
const int inf=INT_MAX;
const int mod=1e9+7;
const int M=1e5+4;
const int N=22;
int n,m,maxn=-inf;
ll ton[N][M],cnt[N][M],c[M];
template<typename type>
inline type cmax(rr type x,rr type y){return x>y?x:y;}
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
};
using namespace STD;
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int a=read();
ton[i][a]++;
maxn=cmax(maxn,a);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=maxn;j++)
for(int k=1;k*j<=maxn;k++)
cnt[i][j]+=ton[i][k*j];
for(rr int i=1;i<=maxn;i++)
{
c[i]=1ll;
for(rr int j=1;j<=n;j++)
c[i]=(c[i]*(cnt[j][i]+1))%mod;
c[i]--;
}
for(int i=maxn;i;i--)
for(int j=2;j*i<=maxn;j++)
c[i]=(c[i]-c[i*j]+mod)%mod;
ll ans=0ll;
for(rr int i=1;i<=maxn;i++)
ans=(ans+c[i]*i%mod)%mod;
printf("%lld\n",ans);
}