HDU 5528
Marry likes to count the number of ways to choose two non-negative integers aa and bbless than mm to make a×ba×b mod m≠0m≠0.
Let’s denote f(m)f(m) as the number of ways to choose two non-negative integers aa and bbless than mm to make a×ba×b mod m≠0m≠0.
She has calculated a lot of f(m)f(m) for different mm, and now she is interested in another function g(n)=∑m|nf(m)g(n)=∑m|nf(m). For example, g(6)=f(1)+f(2)+f(3)+f(6)=0+1+4+21=26g(6)=f(1)+f(2)+f(3)+f(6)=0+1+4+21=26. She needs you to double check the answer.
Give you nn. Your task is to find g(n)g(n) modulo 264264.
Input
The first line contains an integer TT indicating the total number of test cases. Each test case is a line with a positive integer nn.
1≤T≤200001≤T≤20000
1≤n≤1091≤n≤109
Output
For each test case, print one integer ss, representing g(n)g(n) modulo 264264.
Sample Input
2
6
514
Sample Output
26
328194
思路
我们考虑f(n)f(n)怎么计算 ,f(n)f(n)等于n2n2减去是nn的倍数的方案数
令表示是nn的倍数的方案数,则 如何理解呢?我们去枚举gcd(a,N)gcd(a,N)的值,可知这个值会等于NN的某个因子,因此我们枚举的因子,则当gcd(a,N)=dgcd(a,N)=d时,即gcd(ad,Nd)=1gcd(ad,Nd)=1,aa有种取值,由于a只有NN的因子d,则要包含N/dN/d这个因子,那么有多少bb满足呢?即个 。因此h(n)=∑d|nφ(nd)∗dh(n)=∑d|nφ(nd)∗d
所以所求为g(n)=∑d|nf(d)=∑d|nd2−∑d|nh(d)g(n)=∑d|nf(d)=∑d|nd2−∑d|nh(d)
对于第二项,质因子分解后,则∑d|pkh(d)=∑d|pk∑d′|dφ(dd′)d′∑d|pkh(d)=∑d|pk∑d′|dφ(dd′)d′
对于pkpk的因子只有k+1k+1个,即loglog级别,因此总共只有质因子分解的时间
总时间复杂度O(T∗(n√log(n)+log2n))O(T∗(nlog(n)+log2n)) 注意这里质因子分解时用素数去筛(先预处理出素数) 否则TLETLE
本题还有一种思路 我们知道∑d|nφ(d)=n∑d|nφ(d)=n,其卷积形式为φ∗I=I∗φ=idφ∗I=I∗φ=id 其中II为恒等1函数,为单位函数。本题我们要求解的是∑d|nh(n)=∑d|n∑w|dφ(dw)⋅w=∑d|n(φ∗id)(d)=I∗φ∗id=(I∗φ)∗id=id∗id=∑d|nid(d)⋅id(nd)=∑d|nd⋅nd=∑d|nn=n⋅∑d|n1=n乘以(n的因子数目)∑d|nh(n)=∑d|n∑w|dφ(dw)⋅w=∑d|n(φ∗id)(d)=I∗φ∗id=(I∗φ)∗id=id∗id=∑d|nid(d)⋅id(nd)=∑d|nd⋅nd=∑d|nn=n⋅∑d|n1=n乘以(n的因子数目)
几个式子总结:
- ∑d|nφ(d)=n∑d|nφ(d)=n
- ∑d|nφ(nd)∗d=∑gcd(i,n)(1<=i<=n)=∑d|nφ(nd)∗d=∑gcd(i,n)(1<=i<=n)=[0,n)[0,n)中任选两个数a,ba,b,且a∗ba∗b是nn的倍数的方案数
代码
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
bool valid[35000];
int ans[35000];
int tot=0;
void get_prime(int n)
{
tot=0;
memset(valid,true,sizeof(valid));
for(int i=2;i<=n;++i){
if(valid[i]){
ans[++tot]=i;
}
for(int j=1;j<=tot&&ans[j]*i<=n;++j){
valid[ans[j]*i]=false;
if(i%ans[j]==0) break;
}
}
}
int main()
{
//cout<<(1<<30)<<endl;
int t;
get_prime(35000);
//cout<<tot<<endl;
//cin>>t;
scanf("%d",&t);
while(t--)
{
//assert(t>15000);
ll n;
scanf("%lld",&n);
ll ans1=1;
ll ans2=1;
for(int i=1;ans[i]*ans[i]<=n;++i){
ll p[35];
if(n%ans[i]==0){
ll res=0;
ll res1=0;
res1+=1*1;
int k=0;p[0]=1;
while(n%ans[i]==0){
n/=ans[i];
k++;
p[k]=p[k-1]*ans[i];
res1+=(p[k]*p[k]);
}
//cout<<k<<endl;
ans1*=res1;
//assert(ans1<(1LL<<62));
for(int j=0;j<=k;++j){
for(int d=0;d<=j;++d){
if(j>d) res+=(p[j-d]-p[j-d-1])*p[d];
else res+=p[d];
}
}
ans2*=res;
}
}
if(n>1){
ans1*=(1+n*n);
ll res=0;
res+=n+n;
ans2*=res;
}
//ans2=0;
printf("%lld\n",ans1-ans2);
//cout<<ans1-ans2<<endl;
}
return 0;
}