这道题题意很简单,就是每次给你一个 n,让你求所有小于 n 且与 n 互质的数的四次方之和。
好吧,这题看着好像不是很难耶。。。然后我就看到了 n<=10 8 ^8 8,t<=100。。。怎么还是多组数据啊QwQ
那这道题该怎么解呢?首先肯定有很多同学想到了一个东西:分解质因数。但是看上去好像又没什么卵用,因为当 n 是一个质数的时候,小于它的所有数都与它互质,你的复杂度一点没变。
那么我们还可以想到一个方法:你先算出所有数的 n 次方之和,然后减去与它不互质的数的四次方之和不就行了吗?嗯,这个方法看上去是可行的。。。
现在我们就把问题划分成了两部分:快速求出 1~n 的四次方之和与如何快速求出与它不互质的数的四次方之和。显然,第二部分可以在分解质因数后直接用容斥去求,容斥不会的自行去补(逃)。
那么四次方和怎么求呢?其实四次方和是有公式的,没有兴趣看证明的同学可以省掉下面一坨证明:
1 4 ^4 4+2 4 ^4 4+…+n 4 ^4 4=n(n+1)(2n+1)(3n 2 ^2 2+3n-1)/30
下面是为喜欢数学的同学量身打造的证明:
我们先来看一下1 2 ^2 2+2 2 ^2 2+…+n 2 ^2 2的证明:
n 3 ^3 3-(n-1) 3 ^3 3=3n 2 ^2 2-3n+1
(n-1) 3 ^3 3-(n-2) 3 ^3 3=3(n-1) 2 ^2 2-3(n-1)+1
…
1 3 ^3 3-0 3 ^3 3=3·1 2 ^2 2-3·1+1
让我们把这些式子加起来,就会得到一个长式子:
n 3 ^3 3-(n-1) 3 ^3 3+(n-1) 3 ^3 3-(n-2) 3 ^3 3+…+2 3 ^3 3-1 3 ^3 3=3·Σ(i 2 ^2 2)-3·Σi+n
整理得:n 3 ^3 3-1=3(1 2 ^2 2+2 2 ^2 2+…+n 2 ^2 2)+3(1+2+…+n)-n
这时候只需要移项不就可以求出 1 2 ^2 2+2 2 ^2 2+…+n 2 ^2 2 的值了吗?
同样的,1 4 ^4 4+2 4 ^4 4+…+n 4 ^4 4 也可以用此方法来求,具体步骤初中数学水平应该就可以完成,我就不再多说了。
#include<iostream>
#include<stdio.h>
#include<map>
#include<vector>
#include<string.h>
#define mod 1000000007
#define dmod 233333335 //30模1e9+7的逆元
using namespace std;
int n;
vector<int> q;//保存 n 的质因数集合
void shuffle(int N)//分解质因数
{
int LN=N;
for(int i=2;i*i<=LN;i++)
{
if(N==1) break;
if(N%i) continue;
q.push_back(i);
while(N%i==0) N/=i;
}
if(N!=1)
q.push_back(N);
}
long long getsum(long long x) {return x*(x+1)%mod*(x*2+1)%mod*(x*3%mod+x*x%mod*3%mod-1)%mod*dmod%mod;}//getsum(n)=pow(1,4)+pow(2,4)+...+pow(n,4)
long long cal(long long x) {return (getsum(n/x)*x%mod*x%mod*x%mod*x%mod)%mod;}//容斥中当前因数为x时的值
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
q.clear();
scanf("%d",&n);
shuffle(n);
if(n==1)
{cout<<"0\n"; continue;}//这个特判非常重要!
int N=q.size();
long long ans=getsum(n);
for(int i=1;i<(1<<N);i++)//枚举质因子集合
{
long long now=1;int num=0;
for(int j=0;j<N;j++)
if(i&(1<<j))
now*=q[j],num++; //计算质因子积和当前用了几个质因子
if(!num) continue;
if((num&1)==0) ans=(ans+cal(now))%mod;
else ans=(ans-cal(now)+mod)%mod;//标准容斥
}
printf("%lld\n",ans);
}
return 0;
}