【JZOJ 省选模拟】最大公约数

题目

Description
在这里插入图片描述
在这里插入图片描述

Input
从文件 divisor.in 中读入数据。
只有一行,输入一个整数n 。

Output
输出到文件 divisor.out 中。
输出一行,一个整数,表示式子的结果。由于三叶只需要你的验证而不是给出答案,所以你只需要输出答案(式子的值)对 10^+7取模的值。

Sample Input
Sample Input1
3

Sample Input2
985

Sample Output
Sample Output1
15

Sample Output2
349880989

Data Constraint
在这里插入图片描述

思路

首先对式子进行反演
在这里插入图片描述
发现后面那一部分可以整除划分,前面那一部分可以杜教筛。
注意,要预处理多一些否则会T

代码

#include<cstdio>
#define P(x,y) x=(x+y)%mod
#define ll long long
using namespace std;
const int A=1e7,mod=1e9+7,M=1e6+3;
ll n,x,ans,mu[A+9],v[34567890],rf[M],rg[M];
int tot=2*A,ps,p[A+9],f[M],g[M];
bool b[M*10];
ll G(ll n)
{
	if(!n) return 0;
	if(n<=A) return v[A+n];
	int x=n%M; while(rg[x]&&rg[x]!=n)x=(x+1)%M;
	if(rg[x]==n) return v[g[x]];
	int _=g[x]=++tot; rg[x]=n;
	for(ll i=2,j; i<=n; i=j+1)
	{
		j=n/(n/i);
		P(v[_],(i+j)%mod*(j-i+1)%mod*(mod+1>>1)%mod*G(n/i));
	}
	return v[_]=(1-v[_]+mod)%mod;
}
ll F(ll n)
{
	if(!n) return 0;
	if(n<=A) return v[n];
	int x=n%M; while(rf[x]&&rf[x]!=n)x=(x+1)%M;
	if(rf[x]==n) return v[f[x]];
	int _=f[x]=++tot; rf[x]=n;
	for(ll i=1,j; i<=n; i=j+1)
	{
		j=n/(n/i);
		P(v[_],(j-i+1)%mod*G(n/i));
	}
	return v[_];
}
int main()
{
	freopen("divisor.in","r",stdin); freopen("divisor.out","w",stdout);
	mu[1]=1,v[1]=1,v[A+1]=1;
	for(int i=2; i<=A; i++)
	{
		if(!b[i]) mu[p[++ps]=i]=mod-1,v[i]=mod-i+1;
		v[A+i]=(v[A+i-1]+i*mu[i])%mod;
		for(int j=1;j<=ps&&(j==1||i%x)&&i*(x=p[j])<=A;j++)
		{
			b[i*x]=1,mu[i*x]=i%x?(mod-mu[i])%mod:0;
			v[i*x]=i%x?v[i]*v[x]%mod:v[i];
		}
	}
	for(int i=2; i<=A; i++) P(v[i],v[i-1]);
	scanf("%lld",&n);
	for(ll i=1,j,k; i<=n; i=j+1)
	{
		j=n/(k=n/i); k%=mod;
		P(ans,k*(k+1)/2%mod*k%mod*(F(j)-F(i-1)+mod));
	}
	printf("%lld",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值