题目链接:点击这里
题目大意:
题目分析:
本题需要一个重要的转化:
仔细观察上图,我们会发现:当一个点的横坐标和纵坐标的
gcd
\gcd
gcd 大于
1
1
1 时就会被遮挡,即
gcd
(
x
,
y
)
≠
1
\gcd(x,y)≠1
gcd(x,y)=1 的点会被遮住
所以题目所求就是:
2
+
∑
i
=
1
n
∑
j
=
1
n
[
gcd
(
i
,
j
)
=
1
]
2+\sum_{i=1}^n\sum_{j=1}^n[\gcd(i,j)=1]
2+∑i=1n∑j=1n[gcd(i,j)=1](其中2是x=0 || y=0的贡献)
所以便有了最直接的方法
1
1
1 :
我们可以把所有点对的
gcd
\gcd
gcd 都求出来,记
k
[
i
]
[
j
]
=
1
k[i][j]=1
k[i][j]=1 为其
gcd
(
i
,
j
)
=
1
\gcd(i,j)=1
gcd(i,j)=1,其余情况
k
[
i
]
[
j
]
=
0
k[i][j]=0
k[i][j]=0 ,此时题目所求就转化为了求有多少个
k
[
i
]
[
j
]
=
1
k[i][j]=1
k[i][j]=1,我们直接对
k
k
k 数组做一次二维前缀和,然后就可以
O
(
1
)
O(1)
O(1) 查询区域中1的个数了
总体时间复杂度为
𝑂
(
𝑛
2
l
o
g
𝑛
+
𝑡
)
𝑂(𝑛^2 log𝑛+𝑡)
O(n2logn+t)
方法
2
2
2:
我们构造一个数组
f
[
i
]
f[i]
f[i] 表示
g
c
d
(
x
,
y
)
=
𝑖
gcd(x,y)=𝑖
gcd(x,y)=i 的坐标数
我们可以用公约数是
i
i
i 的坐标数
(
n
/
i
)
∗
(
n
/
i
)
(n/i)*(n/i)
(n/i)∗(n/i) 减去
∑
k
=
2
k
∗
i
≤
n
f
[
k
∗
i
]
\sum_{k=2}^{k*i\le n}f[k*i]
∑k=2k∗i≤nf[k∗i]
所以求
f
[
i
]
f[i]
f[i] 的时间复杂度为
𝑂
(
𝑛
l
o
g
𝑛
)
𝑂(𝑛 log𝑛)
O(nlogn) (调和级数)
总体时间复杂度为
𝑂
(
𝑡
⋅
𝑛
l
o
g
𝑛
)
𝑂(𝑡·𝑛 log𝑛)
O(t⋅nlogn)
此方法的优点:代码量小,可处理横纵坐标长度不同的区域
方法
3
3
3:
由于题目所求为:
2
+
∑
i
=
1
n
∑
j
=
1
n
[
gcd
(
i
,
j
)
=
1
]
2+\sum_{i=1}^n\sum_{j=1}^n[\gcd(i,j)=1]
2+∑i=1n∑j=1n[gcd(i,j)=1]
而
∑
i
=
1
n
gcd
(
i
,
i
)
=
1
\sum_{i=1}^n\gcd(i,i)=1
∑i=1ngcd(i,i)=1
所以
∑
i
=
1
n
∑
j
=
1
n
[
gcd
(
i
,
j
)
=
1
]
\sum_{i=1}^n\sum_{j=1}^n[\gcd(i,j)=1]
i=1∑nj=1∑n[gcd(i,j)=1]
=
−
∑
i
=
1
n
gcd
(
i
,
i
)
+
∑
i
=
1
n
∑
j
=
1
i
[
gcd
(
i
,
j
)
=
1
]
=-\sum_{i=1}^n\gcd(i,i)+\sum_{i=1}^n\sum_{j=1}^i[\gcd(i,j)=1]
=−i=1∑ngcd(i,i)+i=1∑nj=1∑i[gcd(i,j)=1]
=
−
1
+
∑
i
=
1
n
∑
j
=
1
i
[
gcd
(
i
,
j
)
=
1
]
=-1+\sum_{i=1}^n\sum_{j=1}^i[\gcd(i,j)=1]
=−1+i=1∑nj=1∑i[gcd(i,j)=1]
=
−
1
+
∑
i
=
1
n
φ
(
i
)
=-1+\sum_{i=1}^n\varphi(i)
=−1+i=1∑nφ(i)
我们可以用线性筛求处欧拉函数,然后对其求前缀和即可
总时间复杂度为
𝑂
(
𝑛
+
𝑡
)
𝑂(𝑛+𝑡)
O(n+t)
在
n
n
n 很大时可以考虑用杜教筛优化求
∑
i
=
1
n
φ
(
i
)
\sum_{i=1}^n\varphi(i)
∑i=1nφ(i) 部分 到
O
(
n
2
3
)
O(n^{\frac{2}{3}})
O(n32)
方法
4
4
4:
求
∑
i
=
1
N
∑
j
=
1
N
[
gcd
(
i
,
j
)
=
1
]
\sum_{i=1}^N\sum_{j=1}^N[\gcd(i,j)=1]
∑i=1N∑j=1N[gcd(i,j)=1] 不难想到可以对其进行莫比乌斯反演(倍数反演) :
设
F
(
n
)
=
(
⌊
N
n
⌋
)
2
F(n)=(\lfloor \frac{N}{n} \rfloor)^2
F(n)=(⌊nN⌋)2 为
n
∣
gcd
(
i
,
j
)
n|\gcd(i,j)
n∣gcd(i,j) 的数量,
f
(
n
)
f(n)
f(n) 为
gcd
(
i
,
j
)
=
n
\gcd(i,j)=n
gcd(i,j)=n 的数量,则有
F
(
n
)
=
∑
n
∣
d
f
(
d
)
F(n)=\sum_{n|d}f(d)
F(n)=∑n∣df(d) ,反演得
f
(
n
)
=
∑
n
∣
d
μ
(
d
n
)
F
(
d
)
=
∑
n
∣
d
μ
(
d
n
)
(
⌊
N
d
⌋
)
2
f(n) = \sum_{n|d}\mu(\frac{d}{n})F(d)=\sum_{n|d}\mu(\frac dn)(\lfloor \frac{N}{d} \rfloor)^2
f(n)=∑n∣dμ(nd)F(d)=∑n∣dμ(nd)(⌊dN⌋)2 ,
所以
f
(
1
)
=
∑
d
=
1
N
μ
(
d
)
(
⌊
N
d
⌋
)
2
f(1)=\sum_{d=1}^N\mu(d)(\lfloor \frac{N}{d} \rfloor)^2
f(1)=∑d=1Nμ(d)(⌊dN⌋)2,线性筛求处
μ
\mu
μ 然后求前缀和,再套个整除分块即可,时间复杂度为
O
(
n
)
O(\sqrt n)
O(n)
方法 1 1 1 代码如下:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<queue>
// #include<unordered_map>
#define ll long long
#define inf 0x3f3f3f3f
#define Inf 0x3f3f3f3f3f3f3f3f
// #define int ll
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
int read()
{
int res = 0,flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
{
res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
ch = getchar();
}
return res*flag;
}
const int maxn = 1e3+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
int n,m,k[maxn][maxn],sum[maxn][maxn],cas;
int gcd(int a,int b)
{
return b ? gcd(b,a%b) : a;
}
int main()
{
for(int i = 1;i < maxn;i++)
for(int j = 1;j < maxn;j++) k[i][j] = gcd(i,j)==1 ? 1 : 0;
for(int i = 1;i < maxn;i++)
for(int j = 1;j < maxn;j++) sum[i][j] = k[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
int t = read();
while(t--)
{
n = read();
printf("%d %d %d\n",++cas,n,sum[n][n]+2);
}
return 0;
}
方法 2 2 2 代码如下:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<queue>
// #include<unordered_map>
#define ll long long
#define inf 0x3f3f3f3f
#define Inf 0x3f3f3f3f3f3f3f3f
// #define int ll
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
int read()
{
int res = 0,flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
{
res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
ch = getchar();
}
return res*flag;
}
const int maxn = 1e3+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
int n,m,f[maxn],cas;
int gcd(int a,int b)
{
return b ? gcd(b,a%b) : a;
}
int main()
{
int t = read();
while(t--)
{
n = read();
int res = n*n;
for(int i = 1;i <= n;i++) f[i] = 0;
for(int i = n;i >= 2;i--)
{
f[i] = (n/i)*(n/i);
for(int j = 2*i;j <= n;j += i) f[i] -= f[j];
res -= f[i];
}
printf("%d %d %d\n",++cas,n,res+2);
}
return 0;
}
方法 3 3 3 代码如下:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<queue>
// #include<unordered_map>
#define ll long long
#define inf 0x3f3f3f3f
#define Inf 0x3f3f3f3f3f3f3f3f
// #define int ll
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
int read()
{
int res = 0,flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
{
res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
ch = getchar();
}
return res*flag;
}
const int maxn = 1e3+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
int n,m,cas,cnt,pri[maxn],phi[maxn];
bool vis[maxn];
void init(int n)
{
phi[1] = 1;
for(int i = 2;i <= n;i++)
{
if(!vis[i]) pri[++cnt] = i,phi[i] = i-1;
for(int j = 1;j<=cnt && i*pri[j]<=n;j++)
{
vis[i*pri[j]] = true;
if(i%pri[j] == 0)
{
phi[i*pri[j]] = phi[i]*pri[j];
break;
}
phi[i*pri[j]] = phi[i]*(pri[j]-1);
}
}
for(int i = 2;i <= n;i++) phi[i] += phi[i-1];
}
int main()
{
init(maxn-1);
int t = read();
while(t--)
{
n = read();
printf("%d %d %d\n",++cas,n,1+2*phi[n]);
}
return 0;
}
方法 4 4 4 代码如下:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<queue>
// #include<unordered_map>
#define ll long long
#define inf 0x3f3f3f3f
#define Inf 0x3f3f3f3f3f3f3f3f
// #define int ll
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
int read()
{
int res = 0,flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
{
res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
ch = getchar();
}
return res*flag;
}
const int maxn = 1e3+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
int n,m,cas,cnt,pri[maxn],mu[maxn],sum[maxn];
bool vis[maxn];
void get_mu() //筛莫比乌斯函数,求需要的函数的前缀和
{
mu[1] = vis[1] = 1;
for(int i = 2;i < maxn;i++)
{
if(!vis[i])
{
mu[i] = -1;
pri[++cnt] = i;
}
for(int j = 1;j <= cnt && i*pri[j] < maxn;j++)
{
vis[i*pri[j]] = true;
if(i%pri[j] != 0) mu[i*pri[j]] -= mu[i];
else break;
}
}
for(int i = 1;i < maxn;i++)
sum[i] = sum[i-1]+mu[i];
}
int main()
{
get_mu();
int t = read();
while(t--)
{
n = read();
int res = 1;
for(int l = 1,r;l <= n;l = r+1)
{
r = (n/(n/l));
res += (sum[r]-sum[l-1])*(n/l)*(n/l);
}
printf("%d %d %d\n",++cas,n,1+res);
}
return 0;
}