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);
}
posted @ 2021-08-13 19:05  Geek_kay  阅读(22)  评论(0编辑  收藏  举报