Description
Input
一行三个数,m, n, k。
Output
Sample Input
样例1
3 3 2
样例2
5000000 5000000 4000000
Sample Output
样例1
20
样例2
913111630
Data Constraint
Solution
- 直接上莫比乌斯反演:
这一步是因为:
∑d|nμ(d)=[n=1] ∑ d | n μ ( d ) = [ n = 1 ]于是我们继续变换:
Ans=∑ddk∑uμ(u)⌊ndu⌋⌊mdu⌋ A n s = ∑ d d k ∑ u μ ( u ) ⌊ n d u ⌋ ⌊ m d u ⌋令 D=du D = d u ,再将 D D 提到前面(这是为了凑形式):
令单位幂函数 idk(d)=dk i d k ( d ) = d k ,那么有:
Ans=∑D⌊nD⌋⌊mD⌋f(D) A n s = ∑ D ⌊ n D ⌋ ⌊ m D ⌋ f ( D )其中:
f(D)=∑d|Didk(d)⋅μ(Dd) f ( D ) = ∑ d | D i d k ( d ) · μ ( D d )是狄利克雷卷积!即: f=idk∗μ f = i d k ∗ μ
对于 f f 这个积性函数,我们可以用线筛求出(跟筛 差不多)。
之后做出前缀和就可以分块求答案了。
这种算法明显优于其它什么两次分块的算法,可以做多组询问。
就是提前给出 k k ,每次询问给出一组 ,范围还是 n,m,k≤5∗106 n , m , k ≤ 5 ∗ 10 6 ,询问数 T≤2000 T ≤ 2000 。
那先预处理出 f f ,每次 回答询问,时间复杂度 O(n+Tn−−√) O ( n + T n ) 。
Code
#include<cstdio>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=5e6+5,mo=1e9+7;
int f[N],g[N];
bool bz[N];
inline int read()
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
inline int ksm(int x,int y)
{
int s=1;
while(y)
{
if(y&1) s=(LL)s*x%mo;
x=(LL)x*x%mo;
y>>=1;
}
return s;
}
inline int min(int x,int y)
{
return x<y?x:y;
}
int main()
{
int n=read(),m=read(),k=read(),ans=0;
g[1]=1;
for(int i=2;i<N;i++)
{
if(!bz[i])
{
f[++f[0]]=i;
g[i]=ksm(i,k)-1;
}
for(int j=1;j<=f[0] && i*f[j]<N;j++)
{
bz[i*f[j]]=true;
if(i%f[j]==0)
{
g[i*f[j]]=(LL)g[i]*(g[f[j]]+1)%mo;
break;
}
g[i*f[j]]=(LL)g[i]*g[f[j]]%mo;
}
}
for(int i=1;i<N;i++) g[i]=(g[i-1]+g[i])%mo;
if(n>m) swap(n,m);
for(int i=1,p;i<=n;i=p+1)
{
p=min(n/(n/i),m/(m/i));
ans=(ans+(LL)(n/p)*(m/p)%mo*(g[p]-g[i-1]+mo)%mo)%mo;
}
printf("%d\n",ans);
return 0;
}