BZOJ 4804: 欧拉心算 欧拉函数
4804: 欧拉心算
Description
给出一个数字N
Input
第一行为一个正整数T,表示数据组数。
接下来T行为询问,每行包含一个正整数N。
T<=5000,N<=10^7
Output
按读入顺序输出答案。
Sample Input
1
10
10
Sample Output
136
思路:
考虑化简原式,设gcd(i,j) = p
则原式可化为
$\sum\limits_{p=1}^{n} \sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n} \varphi(p)[gcd(i,j)=p]$
由于$\varphi(p)$和i、j均无关,故我们可以把$\varphi(p)$提到前面化为
$\sum\limits_{p=1}^{n} \varphi(p)\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n}[gcd(i,j)=p]$
考虑把$[gcd(i,j)=p]$ 中的p除到前面,则原式可化为
$\sum\limits_{p=1}^{n} \varphi(p)\sum\limits_{i=1}^{\lfloor n/p\rfloor} \sum\limits_{j=1}^{\lfloor n/p \rfloor}[gcd(i,j)=1]$
这时我们设一个函数f(n) 表示 $\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)=1]$
则原式可视为 $\sum\limits_{p=1}^{n} \varphi(p)*f(\lfloor n/p\rfloor)$
考虑化简函数f(n) 。我们发现这个函数和欧拉函数相当相像。首先我们只考虑$i\ge j$的情况;
即为
$\sum\limits_{p=1}^{n} \sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n} \varphi(p)[gcd(i,j)=p]$
由于$\varphi(p)$和i、j均无关,故我们可以把$\varphi(p)$提到前面化为
$\sum\limits_{p=1}^{n} \varphi(p)\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n}[gcd(i,j)=p]$
考虑把$[gcd(i,j)=p]$ 中的p除到前面,则原式可化为
$\sum\limits_{p=1}^{n} \varphi(p)\sum\limits_{i=1}^{\lfloor n/p\rfloor} \sum\limits_{j=1}^{\lfloor n/p \rfloor}[gcd(i,j)=1]$
这时我们设一个函数f(n) 表示 $\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)=1]$
则原式可视为 $\sum\limits_{p=1}^{n} \varphi(p)*f(\lfloor n/p\rfloor)$
考虑化简函数f(n) 。我们发现这个函数和欧拉函数相当相像。首先我们只考虑$i\ge j$的情况;
即为
$\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{i}[gcd(i,j)=1] => \sum\limits_{i=1}^{n}\varphi(i)$
同理,我们发现只考虑$i\le j$的情况,
也为$\sum\limits_{j=1}^{n}\varphi(j)$
两种情况同时考虑,发现有一种$i=j$的情况重复,故$f(n)$最终可化简为$2 \times\sum\limits_{i=1}^{n}\varphi(i) -1$
综上所述,我们可以发现$ans = \sum\limits_{p=1}^{n} \varphi(p) \times ( (2 \times\sum\limits_{i=1}^{n}\varphi(i)) -1)$
现在我们有了一个$O(2\times n)$的方法求解$ans$ 但是数据组数为$T = 5000$ .
同理,我们发现只考虑$i\le j$的情况,
也为$\sum\limits_{j=1}^{n}\varphi(j)$
两种情况同时考虑,发现有一种$i=j$的情况重复,故$f(n)$最终可化简为$2 \times\sum\limits_{i=1}^{n}\varphi(i) -1$
综上所述,我们可以发现$ans = \sum\limits_{p=1}^{n} \varphi(p) \times ( (2 \times\sum\limits_{i=1}^{n}\varphi(i)) -1)$
现在我们有了一个$O(2\times n)$的方法求解$ans$ 但是数据组数为$T = 5000$ .
我们需要更加优化的方法才能解决问题,
刚刚的2 * n中有一个n是预处理出$\varphi()$以及其前缀和。
是不和T相乘的,故我们考虑把每个答案$O(\sqrt n)$处理
发现在处理$s[\lfloor n/p\rfloor]$中,$\lfloor n/p\rfloor$在每一段的值都是相同的,共有约$\sqrt n$段,
发现在处理$s[\lfloor n/p\rfloor]$中,$\lfloor n/p\rfloor$在每一段的值都是相同的,共有约$\sqrt n$段,
而根据乘法结合律,我们可以先把$\varphi()$加到一起,
再来乘这个相等的s。s是在预处理中做好的,
故总的时间复杂度为$O(2\times n + T\times \sqrt n)$
下面附上代码
下面附上代码
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 10000001; int phi[N+10], pr[N+10], tot; bool np[N+10]; long long s[N+10]; void getphi() { int i, j; phi[1] = 1; for(i=2;i<=N;i++) { if(!np[i]) { pr[++tot] = i; phi[i] = i-1; } for(j=1;j<=tot&&i*pr[j]<=N;j++) { np[i*pr[j]]=1; if(i%pr[j] == 0) { phi[i*pr[j]]=phi[i]*pr[j]; break; } else phi[i*pr[j]]=phi[i]*(pr[j] - 1); } } for(i=1;i<=N;i++) { s[i]=s[i-1]+phi[i]; } } int main() { int t; scanf("%d",&t); getphi(); while(t--) { int n; long long ans=0; scanf("%d",&n); int lst =0; for(int p=1;p<=n;p=lst+1) { lst = n/(n/p); ans+=(s[lst]-s[p-1])*(s[n/p]*2-1); } printf("%lld\n",ans); } }
欢迎来原博客看看>原文链接<
特别感谢同届神犇@fcwww 给我的帮助