D(m,n)=∑d|m∑k=1nσ0(kd)D(m,n)=∑d|m∑k=1nσ0(kd)
求 D(200!,1012)D(200!,1012)
做的第一道PE题,我终于会反演啦
kdkd 的每个约数可以表示成 kk 的约数乘上 的约数,但是这样一个约数会被表示很多次
假设 a|ka|k,b|db|d,那么如果 gcd(a,b)=1gcd(a,b)=1 就唯一表示了 a×ba×b 了
那么就可以推式子
D(m,n)=∑d|m∑k=1n∑a|d∑b|k[(a,b)=1]D(m,n)=∑d|m∑k=1n∑a|d∑b|k[(a,b)=1]
=∑a|m∑b=1n[(a,b)=1]σ0(ma)⌊nb⌋=∑a|m∑b=1n[(a,b)=1]σ0(ma)⌊nb⌋
=∑t|m,t≤nμ(t)∑a|mtσ0(mat)∑b=1⌊nt⌋⌊nbt⌋=∑t|m,t≤nμ(t)∑a|mtσ0(mat)∑b=1⌊nt⌋⌊nbt⌋
因为 μ(t)≠0μ(t)≠0 对答案才有贡献,所以只要枚举 mm 的素数的子集,使得这些素数乘积小于等于 就行了,200以内的素数只有46个,而第二个限制就使得有效的情况数不会很多
∑nb=1⌊nb⌋∑b=1n⌊nb⌋ 分块算,记忆化一下复杂度大概是 O(n23)O(n23)
∑a|nσ0(na)∑a|nσ0(na) 就是 nn 的所有约数的约数个数的和
稍微推一下就可以知道这个东西等价于 ,n=∏pciin=∏pici
这个在dfs过程中能 O(1)O(1) 处理
跑个20s就跑出来啦
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <map>
#include <ctime>
using namespace std;
typedef long long ll;
const int P=1e9+7,inv2=P+1>>1;
int p[210],mu[210],inv[310];
inline int Pow(int x,int y){
int ret=1;
for(;y;y>>=1,x=1LL*x*x%P) if(y&1) ret=1LL*ret*x%P;
return ret;
}
inline void Pre(const int &n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!p[i]) p[++*p]=i,mu[i]=-1;
for(int j=1;j<=*p && p[j]*i<=n;j++){
p[p[j]*i]=1;
if(i%p[j]) mu[i*p[j]]=-mu[i];
else break;
}
}
for(int i=1;i<=300;i++) inv[i]=Pow(i,P-2);
}
int c[210],prod=1;
ll n;
int m,ans;
inline int f(int x){
return 1LL*(x+1)*(x+2)/2%P;
}
inline int invf(int x){
return 2LL*inv[x+1]*inv[x+2]%P;
}
map<ll,int> M;
inline int g(ll n){
if(M.count(n)) return M[n];
int ret=0;
for(ll i=1,j;i<=n;i=j+1){
j=n/(n/i);
int a=(j-i+1)%P,b=(n/i)%P;
ret=(ret+1LL*a*b)%P;
}
return M[n]=ret;
}
ll tot;
void dfs(int x,int num,ll cc){
if(cc>n) return ;
if(x>*p){
ans=(ans+1LL*num*prod*g(n/cc))%P; return ;
}
dfs(x+1,num,cc);
prod=1LL*prod*invf(c[x])%P*f(c[x]-1)%P; c[x]--;
dfs(x+1,-num,cc*p[x]);
prod=1LL*prod*invf(c[x])%P*f(c[x]+1)%P; c[x]++;
}
int main(){
cin>>m>>n; Pre(m);
for(int i=1;i<=*p;i++){
int cur=m;
while(cur/p[i]) c[i]+=(cur/=p[i]);
prod=1LL*prod*f(c[i])%P;
}
dfs(1,1,1);
printf("%d\n",(ans+P)%P);
printf("%d\n",clock());
return 0;
}