4407: 于神之怒加强版

本文探讨了数论函数的应用及算法优化技巧,包括分块套分块算法和高效的数论函数计算方法。通过具体实例介绍了如何利用这些方法解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

不多讲了,详见数论函数杂讲ppt。

算法一:分块套分块。

写完才发现复杂度为O(nT),是过不了的。

但代码还是要挂的。

#pragma GCC optimize("O2")
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define ll long long
#define pli pair<ll,int>
#define mkp make_pair
#define X first
#define Y second
#define N 5000005
#define MOD 1000000007
ll _,n,m,k,ans;
ll pri[N],cnt,mu[N],Smu[N],dk[N],Sdk[N];bool vis[N];
inline ll add(ll a,ll b){
	ll c=a+b;
	if(c>=MOD)c-=MOD;
	return c;
}
inline ll sub(ll a,ll b){
	ll c=a-b;
	if(c<0)c+=MOD;
	return c;
}
inline ll mul(ll a,ll b){
	return a*b%MOD;
}
ll pw(ll a){
	ll b=k,c=1;
	for(;b;b>>=1,a=mul(a,a))
		if(b&1)c=mul(c,a);
	return c;
}
void shai(){
	ll i,j,v,rg=N-5;
	Smu[1]=mu[1]=dk[1]=Sdk[1]=1;
	rep(i,2,rg){
		if(!vis[i])
			mu[pri[++cnt]=i]=-1,dk[i]=pw(i);
		Smu[i]=add(mu[i],Smu[i-1]);
		Sdk[i]=add(dk[i],Sdk[i-1]);
		for(j=1;j<=cnt&&(v=pri[j]*i)<=rg;j++){
			vis[v]=1;
			dk[v]=mul(dk[i],dk[pri[j]]);
			if(i%pri[j]==0)break;
			mu[v]=-mu[i];
		}
	}
}
ll f(int nn,int mm){
	ll i,j,rg=min(nn,mm),rtn=0;
	for(i=1;i<=rg;i=j+1){
		j=min(nn/(nn/i),mm/(mm/i));
		rtn=add(rtn,mul(sub(Smu[j],Smu[i-1]),mul(nn/i,mm/i)));
	}
	return rtn;
}
int main(){
	scanf("%lld%lld",&_,&k);
	shai();
	ll i,j,rg;
	while(_--){
		scanf("%lld%lld",&n,&m);
		rg=min(n,m);ans=0;
		for(i=1;i<=rg;i=j+1){
			j=min(n/(n/i),m/(m/i));
			ans=add(ans,mul(sub(Sdk[j],Sdk[i-1]),f(n/i,m/i)));
		}
		printf("%lld\n",ans);
	}
	return 0;
}

看完算法二才知道自己原来的思路是对的。。。当时看了算法一觉得自己思路错了,顿时不爽起来,把自己写的代码全删了QAQ。

再把代码挂上。

#pragma GCC optimize("O3")
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define ll long long
#define pli pair<ll,int>
#define mkp make_pair
#define X first
#define Y second
#define N 5000005
#define MOD 1000000007
ll _,n,m,k,ans;
ll pri[N],cnt,mu[N],g[N],Sg[N];bool vis[N];
inline ll add(ll a,ll b){
	ll c=a+b;
	if(c>=MOD)c-=MOD;
	return c;
}
inline ll sub(ll a,ll b){
	ll c=a-b;
	if(c<0)c+=MOD;
	return c;
}
inline ll mul(ll a,ll b){
	return a*b%MOD;
}
ll pw(ll a){
    ll b=k,c=1;
    for(;b;b>>=1,a=mul(a,a))
        if(b&1)c=mul(c,a);
    return c;
}
void shai(){
	ll i,j,v,rg=N-5;
	mu[1]=g[1]=Sg[1]=1;
	rep(i,2,rg){
		if(!vis[i])
			mu[pri[++cnt]=i]=-1,g[i]=sub(pw(i),1);
		for(j=1;j<=cnt&&(v=pri[j]*i)<=rg;j++){
			vis[v]=1;
			if(i%pri[j]==0){
				g[v]=mul(g[i],g[pri[j]]+1);break;
			}
			mu[v]=-mu[i];
			g[v]=mul(g[i],g[pri[j]]);
		}
		Sg[i]=add(Sg[i-1],g[i]);
	}
}
int main(){
	scanf("%lld%lld",&_,&k);
	shai();
	ll i,j,rg;
	while(_--){
		scanf("%lld%lld",&n,&m);
		rg=min(n,m);ans=0;
		for(i=1;i<=rg;i=j+1){
			j=min(n/(n/i),m/(m/i));
			ans=add(ans,mul(mul(n/i,m/i),sub(Sg[j],Sg[i-1])));
		}
		printf("%lld\n",ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值