整理的算法模板合集: ACM模板
母题:UVA11417 GCD
数据很小,我们只需要直接暴力枚举即可。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 5000007;
int gcd(int a, int b)
{
if(b == 0) return a;
return gcd(b, a % b);
}
ll solve(int n)
{
ll res = 0;
for(int i = 1; i <= n; ++ i){
for(int j = i + 1; j <= n; ++ j){
res += gcd(i, j);
}
}
return res;
}
int n;
int main()
{
while(scanf("%d", &n) != EOF && n){
printf("%d\n", solve(n));
}
return 0;
}
拓展一、UVA11426 GCD - Extreme (II)
本题的数据开到了
4
∗
1
0
6
4*10^{6}
4∗106,所以暴力一定TLE。
啊,这,范围太大了,我可以莫比乌斯反演 + 整除分块, O ( n ) O(\sqrt{n}) O(n) 解决!
《算法竞赛入门经典训练指南》上提供了一个构造函数的方法:
我们设 f ( n ) = g c d ( 1 , n ) + g c d ( 2 , n ) + g c d ( 3 , n ) + . . . + g c d ( n − 1 , n ) f(n)=gcd(1,n)+gcd(2,n)+gcd(3,n)+...+gcd(n−1,n) f(n)=gcd(1,n)+gcd(2,n)+gcd(3,n)+...+gcd(n−1,n)
答案很明显就是 S ( n ) = f ( 2 ) + f ( 3 ) + . . . + f ( n ) S(n) = f(2) + f(3) + ... + f(n) S(n)=f(2)+f(3)+...+f(n)
我们首先考虑如何求 f ( n ) f(n) f(n)
我们可以先把 g c d ( x , n ) gcd(x, n) gcd(x,n)的值分类,我们发现 g c d ( x , n ) gcd(x,n) gcd(x,n)肯定是n的约数,再设 g ( n , x ) g(n, x) g(n,x)表示 g c d ( x , n ) = i gcd(x,n)=i gcd(x,n)=i的小于 n n n的正整数的个数。
我们发现 [ g c d ( x , n ) = i ] − > [ g c d ( x / i , n / i ) = = 1 ] [gcd(x, n) = i] -> [gcd(x/i, n/i)== 1] [gcd(x,n)=i]−>[gcd(x/i,n/i)==1], 和 n / i n/i n/i互质的数的个数即为 φ ( n / i ) φ(n/i) φ(n/i)个。
显然 f ( n ) = ∑ i ∣ n i ∗ g ( n , i ) f(n) = \sum _{i|n} i * g(n, i) f(n)=∑i∣ni∗g(n,i)
我们直接求一下 S ( n ) S(n) S(n),既是答案。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 5000007;
ll s[N], primes[N];
ll f[N];
ll n, m, cnt;
ll phi[N];
bool vis[N];
void get_phi(ll n)
{
vis[1] = true;
for(int i = 2; i < n; ++ i){
if(vis[i] == 0){
primes[ ++ cnt] = i;
phi[i] = i - 1;
}
for(int j = 1; j <= cnt && primes[j] * i < n; ++j){
vis[i * primes[j]] = true;
if(i % primes[j] == 0){
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
else phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
}
int main()
{
get_phi(N - 5);
for(int i = 1; i <= N - 1; ++ i){
for(int j = i * 2; j < N; j += i)
f[j] += i * phi[j / i];
}
for(int i = 1; i <= N - 1; ++ i)
s[i] = s[i - 1] + f[i];
while(scanf("%lld\n", &n) == 1 && n){
printf("%lld\n", s[n]);
}
return 0;
}
扩展二、luogu P2398 GCD SUM
这道题要求的式子是: ∑ i = 1 n ∑ j = 1 n g c d ( i , j ) \sum_{i=1}^{n}\sum_{j=1}^{n}{\rm gcd}(i,j) i=1∑nj=1∑ngcd(i,j)
其实就是: ( ∑ i = 1 n ∑ j = 1 n g c d ( i , j ) [ i < j ] ) × 2 + ∑ i = 1 n i \left(\sum_{i=1}^{n}\sum_{j=1}^{n}{\rm gcd}(i,j)[i<j]\right)\times 2 +\sum_{i=1}^{n}i (i=1∑nj=1∑ngcd(i,j)[i<j])×2+i=1∑ni 答案就应该是:
S ( n ) ∗ 2 + ∑ i = 1 n i S(n) * 2 + \sum_{i = 1}^{n}i S(n)∗2+i=1∑ni
S
(
n
)
S(n)
S(n)是上一题的函数。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 100007;
ll s[N], primes[N];
ll f[N];
ll n, m, cnt;
ll phi[N];
bool vis[N];
void get_phi(ll n)
{
vis[1] = true;
for(int i = 2; i < n; ++ i){
if(vis[i] == 0){
primes[ ++ cnt] = i;
phi[i] = i - 1;
}
for(int j = 1; j <= cnt && primes[j] * i < n; ++j){
vis[i * primes[j]] = true;
if(i % primes[j] == 0){
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
else phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
}
int main()
{
get_phi(N - 5);
for(int i = 1; i <= N - 1; ++ i){
for(int j = i * 2; j < N; j += i)
f[j] += i * phi[j / i];
}
for(int i = 1; i <= N - 1; ++ i)
s[i] = s[i - 1] + f[i];
cin >> n;
ll ans = 0;
for(int i =1; i <= n; ++ i)
ans += i;
ans += 2 * s[n];
printf("%lld\n", ans);
return 0;
}
扩展三、luogu P2568 GCD
这道题要求的式子是: ∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) i s p r i m e ] \sum_{i=1}^{n}\sum_{j=1}^{n}[{\rm gcd}(i,j)\tt is\ prime] i=1∑nj=1∑n[gcd(i,j)is prime]
其实就是: ( ∑ i = 1 n s u m p h i ( n i ) [ i i s p r i m e ] ) × 2 − ∑ i = 1 n [ i i s p r i m e ] \left(\sum_{i=1}^{n}{\rm sumphi}(\frac {n}{i})[i\ \tt is\ prime]\right)\times 2-\sum_{i=1}^{n}[i\ \tt is\ prime] (i=1∑nsumphi(in)[i is prime])×2−i=1∑n[i is prime]
我们只需要用线性筛筛一下素数即可。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 10000007;
int n, m;
int primes[N];
int phi[N];
int cnt;
bool vis[N];
ll sum[N];
void init(int n)
{
phi[1] = 0;//这里应该是0
for(int i = 2; i <= n; ++ i){
if(vis[i] == 0){
primes[ ++ cnt] = i;
phi[i] = i - 1;
}
for(int j = 1; j <= cnt && primes[j] * i <= n; ++ j){
vis[i * primes[j]] = true;
if(i % primes[j] == 0){
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
for(int i = 1; i <= n; ++ i)//求phi[n]的前缀和
sum[i] = sum[i - 1] + phi[i];
}
int main()
{
scanf("%d", &n);
init(n);
ll ans = 0;
for(int i = 1; i <= cnt; ++ i){
int p = primes[i];
ans += sum[n / p] * 2 + 1;
}
printf("%lld\n", ans);
return 0;
}