题目大意:
求
∑
i
=
1
n
∑
j
=
1
j
[
g
c
d
(
i
,
j
)
=
=
1
]
d
i
g
(
j
)
\sum_{i=1}^{n}\sum_{j=1}^{j}[gcd(i,j)==1]dig(j)
∑i=1n∑j=1j[gcd(i,j)==1]dig(j)
n
≤
1
e
5
n \leq 1e5
n≤1e5
思路:
方法一: 分段打表.
设 x x x为分段大小.数组长度通常不超过1e3.那么 n x ≤ 1 e 3 \frac{n}{x}\leq1e3 xn≤1e3.那么 x = 100. x=100. x=100.
那么最差时间复杂度为查询 n − 1 n - 1 n−1的情况,复杂度为: O ( n x l o g n ) ≤ 2 e 8 O(nxlogn)\leq2e8 O(nxlogn)≤2e8。差不多擦边过了. 932 m s 932ms 932ms
方法二: 容斥求区间互质
上述式子代表:对于 [ 1 , n ] [1,n] [1,n]的数 x x x,求 [ 1 , x ] [1,x] [1,x]内与其互质的数的数位和。
换一种统计方式:对于一个数 x x x,求出 [ x , n ] [x,n] [x,n]内与它互质的数的个数 ∗ * ∗它的数位和.
那么,求区间互质的数的个数就很好求了,直接上二进制容斥即可.复杂度 O ( 2 7 ∗ n ) O(2^7 * n) O(27∗n). 实际跑 82 m s 82ms 82ms
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
// 求[1 , x]内与d互质的数的个数
// 2 3 5 7 11 13 17
vector<ll> getF(int x)
{
vector<ll> p;
for (int i = 2 ; i * i <= x ; i++){
if (x % i == 0){
p.push_back(i);
while (x % i == 0) x /= i;
}
}
if (x != 1) p.push_back(x);
return p;
}
ll solve (ll x , ll d)
{
vector<ll> p = getF(d);
cout << endl;*/
int len = p.size();
int s = 1 << len;
ll res = 0;
for (int i = 0 ; i < s ; i++){
ll f = 0 , val = 0 , gg = 1;
for (int j = 0 ; j < len ; j++){
if (i & (1 << j)){
f++;
gg *= p[j];
}
}
val += x / gg;
if (f % 2) res -= val;
else res += val;
}
return res;
}
ll cal (int x)
{
ll res = 0;
while(x)
{
res += x % 10;
x /= 10;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
ll n; cin >> n;
// cout << solve(n , 1) << endl;
ll ans = 0;
for (int i = 1 ; i <= n ; i++){
ans += 1ll * cal(i) * (solve(n , i) - solve(i - 1 , i));
}
cout << ans << endl;
return 0;
}
方法三:莫比乌斯反演
听说是入门级别的反演,随缘补吧…