第一个题是BZOJ 2818
要求出
g
c
d
(
x
,
y
)
=
=
i
gcd(x,y) ==i
gcd(x,y)==i,
i
i
i是素数的对数,我们假设
f
(
n
)
f(n)
f(n)为
g
c
d
(
1
,
n
)
…
…
g
c
d
(
n
,
n
)
gcd(1,n)……gcd(n,n)
gcd(1,n)……gcd(n,n)为素数的对数,所以
S
(
n
)
=
S
(
n
−
1
)
+
f
(
n
)
S(n) = S(n - 1) + f(n)
S(n)=S(n−1)+f(n),我们可以将
i
i
i带进去得到
g
c
d
(
x
i
,
n
i
)
=
=
1
gcd(\frac{x}{i},\frac{n}{i} ) == 1
gcd(ix,in)==1,则满足该式子的
x
x
x有
φ
(
n
/
i
)
φ(n/i)
φ(n/i)个,因为他要求的每对数交换后的结果亦可以,所以
f
(
n
)
=
2
∗
φ
(
n
/
i
)
f(n) = 2*φ(n/i)
f(n)=2∗φ(n/i) ,所以我们可以先求出欧拉函数的前缀和,然后直接枚举素数,用上面的式子计算出结果。
或者用另一种理解方式,我们知道1~n内互质的数的对数其实就是欧拉函数的前缀和,
g
c
d
(
x
i
,
y
i
)
=
=
1
gcd(\frac{x}{i},\frac{y}{i} ) == 1
gcd(ix,iy)==1,从中可以知道两者的上限是
n
i
\frac{n}{i}
in,所以就转化成了求出1 - n/i的互质的数的对数,枚举i即可。
注意这道题卡long long 该开long long 的开 不用的就不要开,不然超时。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e7 + 13;
#define int long long
int f[N],phi[N],prime[N], vis[N],sum[N];
int len;
void init(int n){
memset(vis, 0, sizeof vis);
len = 0;
for(int i = 2; i <= 10000000; i ++){
if(vis[i] == 0){
vis[i] = i, prime[ ++len] = i;
phi[i] = i - 1;
}
for(int j = 1; j <= len; j ++){
if(prime[j] > vis[i] || prime[j] > 10000000/i) break;
vis[i*prime[j]] = prime[j];
phi[i*prime[j]] = phi[i] * (i%prime[j]? prime[j] - 1: prime[j]);
}
}
sum[1] = 1;
for(int i = 2; i <= 10000000; i ++){
sum[i] = sum[i - 1] + 2*phi[i];
}
}
signed main()
{
int n;
cin >> n;
init(n);
int ans = 0;
for(int i = 2; i <= n; i ++){
if(vis[i] == i){
ans += sum[n/i];
}
}
cout << ans << endl;
}
第二个题是UVA 11426(因为UVA要挂梯子所以就给了vj的链接)
这个题和上面的题目思路和上面差不多,同样假设
f
(
n
)
=
g
c
d
(
1
,
n
)
+
…
…
+
g
c
d
(
n
,
n
)
f(n)=gcd(1,n)+……+gcd(n,n)
f(n)=gcd(1,n)+……+gcd(n,n),所以
S
(
n
)
=
f
(
1
)
+
f
(
2
)
…
…
+
f
(
n
)
S(n) = f(1) + f(2)…… + f(n)
S(n)=f(1)+f(2)……+f(n),我们假设i的个数为
g
(
n
,
i
)
g(n,i)
g(n,i),那么i的贡献度就是
i
∗
g
(
n
,
i
)
i*g(n,i)
i∗g(n,i),将
i
i
i带进gcd得到
g
c
d
(
x
i
,
n
i
)
=
=
1
gcd(\frac{x}{i},\frac{n}{i} ) == 1
gcd(ix,in)==1,满足的x就有
φ
(
n
/
i
)
φ(n/i)
φ(n/i)个,所以
g
(
n
,
i
)
=
φ
(
n
/
i
)
g(n,i) = φ(n/i)
g(n,i)=φ(n/i),我们当然不可以直接枚举i所以,可以像埃筛一样,对于每个数求出他对每个倍数的贡献度.
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 4000000 + 5;
ll sum[N] , f[N];
int phi[N] ;
int len = 0;
void Euler(){
for(int i=1;i<= N - 5;i++)
phi[i]=i;
for(int i=2;i<= N - 5;i+=2)
phi[i]=phi[i]/2;
for(int i=3;i<=N - 5;i+=2){
if(phi[i] == i){
for(int j=i;j<=N - 5;j+=i)
phi[j]=phi[j]/i*(i-1);
}
}
}
void init(){
for(int i = 1; i <= N - 5; i ++){
for(int j = 2*i; j <= N - 5; j += i){
f[j] += i*phi[j/i];
}
}
sum[2] = f[2];
for(int i = 3; i <= N - 5; i ++){
sum[i] = sum[i - 1] + f[i];
}
}
int main()
{
Euler();
init();
int n;
while(cin >> n,n){
cout << sum[n] << endl;
}
}
上面这两个可以用同一种推到思路。
第三个题是BZOJ 1101
这个题的i,j限制不再是相同的,我们只能用莫比乌斯函数.
附一篇大佬博客
看了就会了。。。。。。。。。。。。
其中的查询时的计算复杂度是
O
(
N
)
O(\sqrt{N})
O(N),即分块,将相同的
n
/
i
n/i
n/i一起算,然后跳到下一块
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 50000 + 10;
int miu[N],sum[N],vis[N];
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void mobius(){
for(int i = 1; i <= 50000; i ++) miu[i] = 1, vis[i] = 0;
for(int i = 2; i <= 50000; i ++){
if(vis[i]) continue;
miu[i] = -1;
for(int j = 2*i; j <= 50000; j += i){
vis[j] = 1;
if((j/i)%i == 0)miu[j] = 0;
else miu[j] *= -1;
}
}
for(int i = 1; i <= 50000; i ++){
sum[i] = sum[i - 1] + miu[i];
}
}
int main()
{
mobius();
int t;
scanf("%d",&t);
while(t --){
int a,b,k;
scanf("%d%d%d",&a,&b,&k);
int ans = 0;
a /= k,b /= k;
for(int i = 1; i <= min(a, b); i ++){
int j = min(a/(a/i), b/(b/i));
ans += (sum[j] - sum[i - 1])*(a/i)*(b/i);
i = j;
}
printf("%d\n",ans);
}
}