博客园同步
原题链接
前置知识:
整除分块
线性筛模板
对于 30 % 30 \% 30% 的数据, n ≤ 3 × 1 0 3 n \leq 3 \times 10^3 n≤3×103.
直接模拟就好了。
时间复杂度: O ( n 2 log n ) O(n^2 \log n) O(n2logn). 实际得分: 30 p t s 30pts 30pts.
for(i from 1 to n)
for(j from 1 to n)
s+=gcd(i,j)
print(s)
对于 60 % 60 \% 60% 的数据, 7000 ≤ n ≤ 7100 7000 \leq n \leq 7100 7000≤n≤7100.
注意到 O ( n 2 log n ) O(n^2 \log n) O(n2logn) 无法通过了。但是,我们可以计算出 n = 7000 n = 7000 n=7000 的答案为 275797760 275797760 275797760. 然后,对于 7001 7001 7001 ~ n n n 区间的答案,实际上就是 两倍的该区间与 1 ≤ i ≤ 7000 1 \leq i \leq 7000 1≤i≤7000 的两两 gcd \gcd gcd 之和,再加上自己和自己区间答案。
???
就比方说, a a a 和 b b b 表示区间,定义 a × b a \times b a×b 为 a a a 区间与 b b b 区间两两 gcd \gcd gcd 和。那么, x = a + b x = a + b x=a+b,则 x 2 = ( a + b ) 2 = a 2 + 2 × a × b + b 2 x^2 = (a+b)^2 = a^2 + 2 \times a \times b + b^2 x2=(a+b)2=a2+2×a×b+b2.
而 a 2 a^2 a2 的答案被提前算出,然后 b 2 b^2 b2 区间长度 ≤ 100 \leq 100 ≤100 结束, a × b a \times b a×b 计算也只需要 7000 × 100 = 7 × 1 0 5 7000 \times 100 = 7 \times 10^5 7000×100=7×105 再加个 log \log log 的时间,过了。
时间复杂度: O ( wys ) O(\text{wys}) O(wys). 实际得分: 60 p t s 60pts 60pts.
s=275797760
for(i from 1 to 7000)
for(j from 7001 to n)
s+=gcd(i,j)*2
for(i from 7001 to n)
for(j from 7001 to n)
s+=gcd(i,j)
print(s)
显然本题可以用 ϕ \phi ϕ 过,但是我们用 莫比乌斯反演!
优化时间复杂度常常需要从零开始。 —— 《深入浅出程序设计竞赛 - 基础篇》 某句改编
对,下面是推式子时间。
∑ i = 1 n ∑ j = 1 n gcd ( i , j ) \sum_{i=1}^n \sum_{j=1}^n \gcd(i,j) i=1∑nj=1∑ngcd(i,j)
= ∑ d = 1 n d ∑ i = 1 n ∑ j = 1 n [ gcd ( i , j ) = = d ] = \sum_{d=1}^n d \sum_{i=1}^n \sum_{j=1}^n [\gcd(i,j)==d] =d=1∑ndi=1∑nj=1∑n[gcd(i,j)==d]
= ∑ d = 1 n d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ [ gcd ( i , j ) = = 1 ] = \sum_{d=1}^n d \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j=1}^{\lfloor \frac{n}{d} \rfloor} [\gcd(i,j)==1] =d=1∑ndi=1∑⌊dn⌋j=1∑⌊dn⌋[gcd(i,j)==1]
= ∑ d = 1 n d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ ∑ k ∣ gcd ( i , j ) μ k = \sum_{d=1}^n d \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j=1}^{\lfloor \frac{n}{d} \rfloor} \sum_{k|\gcd(i,j)} \mu_k =d=1∑ndi=1∑⌊dn⌋j=1∑⌊dn⌋k∣gcd(i,j)∑μk
= ∑ d = 1 n d ∑ k = 1 n ⌊ n T ⌋ 2 μ k ( T = d × k ) = \sum_{d=1}^n d \sum_{k=1}^n \lfloor \frac{n}{T} \rfloor^2 \mu_k (T = d \times k) =d=1∑ndk=1∑n⌊Tn⌋2μk(T=d×k)
= ∑ T = 1 n ∑ d ∣ T d × μ T d ⌊ n T ⌋ 2 = \sum_{T=1}^n \sum_{d|T} d \times \mu_\frac{T}{d} \lfloor \frac{n}{T} \rfloor^2 =T=1∑nd∣T∑d×μdT⌊Tn⌋2
= ∑ T = 1 n ϕ ( T ) ⌊ n T ⌋ 2 = \sum_{T=1}^n \phi(T) \lfloor \frac{n}{T} \rfloor^2 =T=1∑nϕ(T)⌊Tn⌋2
每一步都是运用了 μ \mu μ 的基本性质,对最后一步的操作讲解一下:
你发现 ∑ d ∣ T d × μ T d \sum_{d|T} d \times \mu_{\frac{T}{d}} ∑d∣Td×μdT 这玩意儿很像 I d ∗ μ Id * \mu Id∗μ,然后就是了,而 I d ∗ μ = ϕ Id * \mu = \phi Id∗μ=ϕ,是不是很神奇? 实际上是一个隐蔽的性质
推式子的基本要点总结:
枚举 gcd \gcd gcd 并降枚举上限。
用 μ \mu μ 套互质,然后瞬间和中间两个 ∑ \sum ∑ 说再见。
再换元,更改循环顺序,用奇特的性质再消掉一个 ∑ \sum ∑。
观察模拟。
本题到了 ∑ T = 1 n ϕ ( T ) ⌊ n T ⌋ 2 \sum_{T=1}^n \phi(T) \lfloor \frac{n}{T} \rfloor^2 ∑T=1nϕ(T)⌊Tn⌋2 这步,直接枚举即可通过了。
时间复杂度: O ( n ) O(n) O(n). 实际得分: 100 p t s 100pts 100pts.
考虑推式子之后一个加强。
题意基本不变,改范围为:
T T T 组询问 n n n 的答案, T ≤ 5 × 1 0 5 T \leq 5 \times 10^5 T≤5×105, n ≤ 5 × 1 0 5 n \leq 5 \times 10^5 n≤5×105.
显然 O ( n × T ) O(n \times T) O(n×T) 过不了。
考虑 ⌊ n T ⌋ 2 \lfloor \frac{n}{T} \rfloor^2 ⌊Tn⌋2 可以整除分块,然后 ϕ \phi ϕ 可以预处理做前缀和。
这样可以单组询问 O ( n ) O(\sqrt{n}) O(n),预处理 O ( n ) O(n) O(n).
此处应当有掌声!
时间复杂度: O ( n + T n ) O(n + T \sqrt{n}) O(n+Tn).
实际得分: 100 p t s 100pts 100pts.
#pragma GCC optimize(2)
#include
using namespace std;
typedef long long ll;
const int N=1e5+1;
inline int read(){char ch=getchar(); int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
ll s[N]; bool h[N];
int phi[N],prime[N],cnt=0;
int n; ll ans=0;
inline void Euler(int n) {
phi[1]=1; s[1]=1;
for(int i=2;i<=n;i++) {
if(!h[i]) phi[i]=i-1,prime[++cnt]=i;
for(int j=1;j<=cnt && prime[j]*i<=n;j++) {
h[i*prime[j]]=1;
if(i%prime[j]==0) {phi[i*prime[j]]=phi[i]*prime[j]; break;}
phi[prime[j]*i]=phi[i]*(prime[j]-1);
} s[i]=s[i-1]+phi[i];
}
} //预处理 phi 和 s (s 为前缀和)
int main() {
Euler(N-1); n=read();
for(int i=1;i<=n;) {
ll t=n/(n/i);
ans+=1ll*(n/t)*(n/t)*(s[t]-s[i-1]);
i=t+1; //整除分块板子
} printf("%lld\n",ans);
return 0;
}
附:本题没有取模,这是令人惊讶的一点——一般的大式子都要取模的。为什么呢?
因为, n = 1 0 5 n=10^5 n=105 时答案才 72434344904 72434344904 72434344904,而答案总不会超过 n 3 n^3 n3 (每个 gcd ≤ n \gcd \leq n gcd≤n 哇,非常粗略的估算)。
n = 1 0 6 → a n s = 8643257847824 n=10^6 \rightarrow ans = 8643257847824 n=106→ans=8643257847824.
n = 1 0 7 → a n s = 1004297420038032 n=10^7 \rightarrow ans = 1004297420038032 n=107→ans=1004297420038032.
看到了吧,我们的估计大的多了, long long \text{long long} long long 没有问题的。