jzoj 4379.【GDOI2016模拟3.10】盈虚方田

题目

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

2
10 10 0
10 10 1

Sample Output

63
93

Data Constraint

在这里插入图片描述


题解

不妨设n<m
q ( l i m , n ) = 1 q(lim,n)=1 q(lim,n)=1表示n的质因数个数不大于lim, q ( l i m , n ) = 0 q(lim,n)=0 q(lim,n)=0表示n的质因数个数大于lim。
显然题目要我们求的是 ∑ i = 1 n ∑ j = 1 m q ( g c d ( i , j ) , l i m ) \sum_{i=1}^n\sum_{j=1}^m q(gcd(i,j),lim) i=1nj=1mq(gcd(i,j),lim)这样直接暴力做是只有30分的,因此考虑化简。
∑ i = 1 n ∑ j = 1 m q ( g c d ( i , j ) , l i m ) = ∑ d = 1 n q ( d , l i m ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = 1 ] 把gcd提前 = ∑ d = 1 n q ( d , l i m ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ p ∣ i , p ∣ j μ ( p ) 下文会解释 = ∑ d = 1 n q ( d , l i m ) ∑ p = 1 ⌊ n d ⌋ μ ( p ) ⌊ ⌊ n d ⌋ p ⌋ ⌊ ⌊ m d ⌋ p ⌋ = ∑ d = 1 n q ( d , l i m ) ∑ p = 1 ⌊ n d ⌋ μ ( p ) ⌊ n p d ⌋ ⌊ m p d ⌋ 下文会解释 \begin{aligned} &amp;\sum_{i=1}^n\sum_{j=1}^m q(gcd(i,j),lim)\\ =&amp;\sum_{d=1}^n q(d,lim)\sum_{i=1}^{\left\lfloor\frac{n}{d}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{d}\right\rfloor}[gcd(i,j)=1]&amp;\text{把gcd提前}\\ =&amp;\sum_{d=1}^n q(d,lim)\sum_{i=1}^{\left\lfloor\frac{n}{d}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{d}\right\rfloor}\sum_{p|i,p|j}\mu(p)&amp;\text{下文会解释}\\ =&amp;\sum_{d=1}^n q(d,lim)\sum_{p=1}^{\left\lfloor\frac{n}{d}\right\rfloor}\mu(p)\left\lfloor\cfrac{\left\lfloor\frac{n}{d}\right\rfloor}{p}\right\rfloor\left\lfloor\cfrac{\left\lfloor\frac{m}{d}\right\rfloor}{p}\right\rfloor\\ =&amp;\sum_{d=1}^n q(d,lim)\sum_{p=1}^{\left\lfloor\frac{n}{d}\right\rfloor}\mu(p)\left\lfloor\frac{n}{pd}\right\rfloor\left\lfloor\frac{m}{pd}\right\rfloor&amp;\text{下文会解释} \end{aligned} ====i=1nj=1mq(gcd(i,j),lim)d=1nq(d,lim)i=1dnj=1dm[gcd(i,j)=1]d=1nq(d,lim)i=1dnj=1dmpi,pjμ(p)d=1nq(d,lim)p=1dnμ(p)pdnpdmd=1nq(d,lim)p=1dnμ(p)pdnpdmgcd提前下文会解释下文会解释
先来解释一下上面的化简步骤。
先是第二步:

h i h_i hi表示 i 是x,y公因数的对数。
那么两两互质的就有 h 1 − h 2 − h 3 + h 6 − h 5 ⋯ h_1-h_2-h_3+h_6-h_5\cdots h1h2h3+h6h5个。
是不是很像莫比乌斯反演

再解释最后一步:

取整函数有一个有趣的性质: ⌊ ⌊ n a ⌋ b ⌋ = ⌊ n a b ⌋ \left\lfloor\cfrac{\left\lfloor\frac{n}{a}\right\rfloor}{b}\right\rfloor=\left\lfloor\cfrac{n}{ab}\right\rfloor ban=abn
证明不难。

b = p d b=pd b=pd,那么又可以进一步变形了(下面的大括号表示解决方法)
= ∑ b = 1 n ⌊ n p d ⌋ ⌊ m p d ⌋ ⎵ 数论分块 ∑ d ∣ b q ( d , l i m ) μ ( b d ) ⎵ 前缀和 =\underbrace{\sum_{b=1}^n \left\lfloor\frac{n}{pd}\right\rfloor\left\lfloor\frac{m}{pd}\right\rfloor}_{\text{数论分块}}\underbrace{\sum_{d|b}q(d,lim)\mu\left(\frac{b}{d}\right)}_{\text{前缀和}} =数论分块 b=1npdnpdm前缀和 dbq(d,lim)μ(db)
数论分块可以看这里
前缀和的话令 S ( n , l i m ) = ∑ i = 1 n ∑ d ∣ n q ( d , l i m ) μ ( n d ) S(n,lim)=\sum_{i=1}^n\sum_{d|n}q(d,lim)\mu\left(\frac{n}{d}\right) S(n,lim)=i=1ndnq(d,lim)μ(dn)
然后枚举一个L,再枚举刚好有L个质因数的数 i ,枚举 i 的每一个倍数 j ,把 S ( j , L ) S(j,L) S(j,L)加上 μ ( j i ) \mu(\frac{j}{i}) μ(ij)
然后处理询问就可以了。


水法

看这条式子 ∑ d = 1 n q ( d , l i m ) ∑ p = 1 ⌊ n d ⌋ μ ( p ) ⌊ n p d ⌋ ⌊ m p d ⌋ \sum_{d=1}^n q(d,lim)\sum_{p=1}^{\left\lfloor\frac{n}{d}\right\rfloor}\mu(p)\left\lfloor\frac{n}{pd}\right\rfloor\left\lfloor\frac{m}{pd}\right\rfloor d=1nq(d,lim)p=1dnμ(p)pdnpdm
可以把莫比乌斯函数用前缀和维护(直接暴力加起来),后面的整除数论分块
然后暴力 O ( n p n ) O(np\sqrt{n}) O(npn )乱搞,显然TLE。
怎么办呢?考虑分类讨论:

  1. P ≤ 3 P\leq3 P3时,暴力做不会时超。
  2. P &gt; 3 P&gt;3 P>3时,把不符合条件的p给删掉,剩下的就是符合条件的了。

于是就可以600+ms水过去了。


CODE

正解:

#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define N 500005
ll prime[50000],u[N],f[N],s[N][20],a[N],nex[N],fir[N];
bool b[N];
inline ll mymin(ll x,ll y){return x<y?x:y;}
int main()
{
	ll n,m,lim,i,j,k,t,l,r,ans=0;
	u[1]=1,f[1]=0,fir[0]=1;
	for(i=2;i<N;i++)
	{
		if(!b[i]) prime[++prime[0]]=i,f[i]=1,u[i]=-1;
		for(j=1;j<=prime[0]&&i*prime[j]<N;j++)
		{
			b[i*prime[j]]=1;
			f[i*prime[j]]=f[i]+1;
			if(i%prime[j]==0) break;
			u[i*prime[j]]=-u[i];
		}
		nex[i]=fir[f[i]],fir[f[i]]=i;
	}
	for(j=0;j<19;j++)
	{
		for(i=fir[j];i;i=nex[i])
			for(k=i;k<N;k+=i)
				a[k]+=u[k/i];
		for(i=1;i<N;i++)
			s[i][j]=s[i-1][j]+a[i];
	}
	scanf("%lld",&t);
	while(t--)
	{
		scanf("%lld%lld%lld",&n,&m,&lim);
		lim=mymin(lim,19);
		if(n>m) n^=m,m^=n,n^=m;
		if(1ll<<lim>n){printf("%lld\n",n*m);continue;}
		l=1,ans=0;
		while(l<=n)
		{
			r=mymin(n/(n/l),m/(m/l));
			ans+=n/l*(m/l)*(s[r][lim]-s[l-1][lim]);
			l=r+1;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

水法:

//摘自DL蔑蔑
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define ll long long
using namespace std;
int su[100010],mu[500010],chun[500010],qian[500010];
bool b[500010];
int FY(int n,int m,int p)
{
	int re=0;
	if (n>m) swap(n,m);
	n/=p;
	m/=p;
	for (int i=1;i<=n;i++)
	{
		int ii=min(n/(n/i),m/(m/i));
		re+=(qian[ii]-qian[i-1])*(n/i)*(m/i);
		i=ii;
	}
	return re;
}
int main()
{
	mu[1]=1;
	chun[1]=0;
	for (int i=2;i<=500000;i++)
	{
		if (!b[i])
		{
			su[++su[0]]=i;
			mu[i]=-1;
			chun[i]=1;
		}
		for (int j=1;j<=su[0];j++)
		if (i*su[j]<=500000)
		{
			b[i*su[j]]=true;
			chun[i*su[j]]=chun[i]+1;
			if (i%su[j]==0)
			{
				mu[i*su[j]]=0;
				break;
			}
			else mu[i*su[j]]=-mu[i];
		}
		else break;
	}
	for (int i=1;i<=50000;i++)
	qian[i]=qian[i-1]+mu[i];
	int T;
	scanf("%d",&T);
	while (T--)
	{
		int n,m,fw,ans=0;
		scanf("%d%d%d",&n,&m,&fw);
		if (fw>=19)
		{
			printf("%lld\n",(ll)n*(ll)m);
			continue;
		}
		int nn=min(n,m);
		if (fw<3)
		{
			for (int p=1;p<=nn;p++)
			if (chun[p]<=fw)
			{
				ans+=FY(n,m,p);
			}
			printf("%d\n",ans);
		}
		else
		{
			ll lans=(ll)n*(ll)m;
			for (int p=1;p<=nn;p++)
			if (chun[p]>fw)
			{
				lans-=(ll)FY(n,m,p);
			}
			printf("%lld\n",lans);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值