记录一个菜逼的成长。。
###2017 Multi-University Training Contest - Team 8
题目链接
题目大意:
让你求
f
(
n
)
=
∑
i
=
1
n
∑
j
=
1
n
⌈
i
j
⌉
[
(
i
,
j
)
=
1
]
f(n) = \sum_{i = 1}^{n} \sum_{j = 1}^{n} \lceil \frac{i}{j} \rceil [(i, j) = 1]
f(n)=i=1∑nj=1∑n⌈ji⌉[(i,j)=1]
理解了q巨的解法后,对反演理解了点。。
以下是q巨说的解法:
记
g
(
i
)
=
∑
j
=
1
n
⌈
i
j
⌉
g(i) = \sum_{j = 1}^{n} \lceil \frac{i}{j} \rceil
g(i)=∑j=1n⌈ji⌉,
h
(
i
)
=
∑
j
=
1
n
⌈
i
j
⌉
[
(
i
,
j
)
=
1
]
h(i) = \sum_{j = 1}^{n} \lceil \frac{i}{j} \rceil [(i, j) = 1]
h(i)=∑j=1n⌈ji⌉[(i,j)=1]
那么
g
(
i
)
=
∑
d
∣
i
h
(
d
)
g(i) = \sum_{d|i}h(d)
g(i)=∑d∣ih(d)
//求解
g
(
i
)
g(i)
g(i)我是枚举
j
(
2
<
=
j
<
=
N
)
j(2 <= j <= N)
j(2<=j<=N)和
⌈
i
j
⌉
\lceil \frac{i}{j} \rceil
⌈ji⌉,然后可以知道
[
(
j
−
1
)
∗
i
+
1
,
j
∗
i
]
[(j-1)*i+1,j*i]
[(j−1)∗i+1,j∗i]这段区间是可以一起处理的,因为这段区间除以
j
j
j后的结果都一样。
我们只需
g
(
(
j
−
1
)
∗
i
+
1
)
+
=
j
,
g
(
j
∗
i
+
1
)
−
=
j
g((j-1)*i+1) += j,g(j*i+1) -= j
g((j−1)∗i+1)+=j,g(j∗i+1)−=j,然后前缀和就是要求的
g
(
i
)
g(i)
g(i)
现在我们已经知道了
g
(
i
)
g(i)
g(i),要求
h
(
i
)
h(i)
h(i)。我们可以用莫比乌斯反演。
可知
h
(
i
)
=
∑
d
∣
i
μ
(
d
)
g
(
i
/
d
)
h(i) = \sum_{d|i}\mu(d)g(i/d)
h(i)=∑d∣iμ(d)g(i/d)
如果你直接两个for循环枚举
i
i
i和
i
i
i的因子是会T的。
这里我们第一个for可以枚举
d
d
d,然后第二个for枚举
i
i
i每次加
d
d
d,可以在
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的时间内处理出来(应该是这个复杂度。。)
然后对
h
(
i
)
h(i)
h(i)求前缀和就是
f
(
i
)
f(i)
f(i)
然后
O
(
1
)
O(1)
O(1)查询
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 1000000 + 10;
LL g[maxn],h[maxn],f[maxn];
bool check[maxn];
int mu[maxn],prime[maxn];
void Mobius()
{
cl(check,false);
mu[1] = 1;
int tot = 0;
for( int i = 2; i < maxn; i++ ){
if(!check[i]){
prime[tot++] = i;
mu[i] = -1;
}
for( int j = 0; j < tot; j++ ){
if(i * prime[j] >= maxn)break;
check[i*prime[j]] = true;
if(i % prime[j] == 0){
mu[i * prime[j]] = 0;
break;
}
else {
mu[i * prime[j]] = -mu[i];
}
}
}
}
void init(int N)
{
for (int i = 2; i < N; i++) {
for (int j = 2; (j-1)*i+1 < N; j++) {
g[(j-1)*i+1] += j;
g[min(N,j*i+1)] -= j;
}
}
for( int i = 1; i < N; i++ ){
g[i] += g[i-1];
g[i] %= MOD;
}
for( int i = 1; i < N; i++ ){
g[i] += (i != 1 ? i+1 : i);
g[i] %= MOD;
}
for( int j = 1; j < N; j++ ){
for( int i = j; i < N; i += j ) {
h[i] = (h[i] + mu[j] * g[i/j]) % MOD;
}
}
for( int i = 1; i < N; i++ )
f[i] = (f[i-1] + h[i]) % MOD;
}
int main()
{
//fin,fout;
Mobius();
init(1000001);
int n;
while(~scanf("%d",&n)){
printf("%lld\n",f[n]);
}
return 0;
}