这道题题意很简单,就是每次给你一个 n,让你求所有小于 n 且与 n 互质的数的四次方之和。
好吧,这题看着好像不是很难耶。。。然后我就看到了 n<=108^88,t<=100。。。怎么还是多组数据啊QwQ
那这道题该怎么解呢?首先肯定有很多同学想到了一个东西:分解质因数。但是看上去好像又没什么卵用,因为当 n 是一个质数的时候,小于它的所有数都与它互质,你的复杂度一点没变。
那么我们还可以想到一个方法:你先算出所有数的 n 次方之和,然后减去与它不互质的数的四次方之和不就行了吗?嗯,这个方法看上去是可行的。。。
现在我们就把问题划分成了两部分:快速求出 1~n 的四次方之和与如何快速求出与它不互质的数的四次方之和。显然,第二部分可以在分解质因数后直接用容斥去求,容斥不会的自行去补(逃)。
那么四次方和怎么求呢?其实四次方和是有公式的,没有兴趣看证明的同学可以省掉下面一坨证明:
14^44+24^44+…+n4^44=n(n+1)(2n+1)(3n2^22+3n-1)/30
下面是为喜欢数学的同学量身打造的证明:
我们先来看一下12^22+22^22+…+n2^22的证明:
n3^33-(n-1)3^33=3n2^22-3n+1
(n-1)3^33-(n-2)3^33=3(n-1)2^22-3(n-1)+1
…
13^33-03^33=3·12^22-3·1+1
让我们把这些式子加起来,就会得到一个长式子:
n3^33-(n-1)3^33+(n-1)3^33-(n-2)3^33+…+23^33-13^33=3·Σ(i2^22)-3·Σi+n
整理得:n3^33-1=3(12^22+22^22+…+n2^22)+3(1+2+…+n)-n
这时候只需要移项不就可以求出 12^22+22^22+…+n2^22 的值了吗?
同样的,14^44+24^44+…+n4^44 也可以用此方法来求,具体步骤初中数学水平应该就可以完成,我就不再多说了。
#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;
}