t a g tag tag:暴力 dfs ???
我们首先没有思路,于是考虑拿出一个数 n n n 对她进行质因数分解:
n = ∏ p i k i n = \prod_{}p_i^{k_i} n=∏piki
再经过一些观察可以发现,如果对于 p i p_i pi 从小到大排序过后,对于一个反素数而言, k i k_i ki 是单调不增的。
这个其实挺好证明的,因为如果 k i k_i ki 单调递增了( k i > k i − 1 k_i > k_{i - 1} ki>ki−1),那么我们交换 k i k_i ki 和 k i − 1 k_{i - 1} ki−1,那么我们就得到了一个因数个数与原数相同的且值比原数小的数 n ′ n' n′,这样一来 n n n 就不满足反素数的定义了,证毕。
于是我们可以得出一个性质,那么就是 1 e 9 1e9 1e9 反素数一定由前 10 10 10 个质数组成(这个很显然吧)。那么我们就可以愉快的 d f s dfs dfs 了,就是尝试确定每一个质数的指数,同时记录一个约数个数就好了。
#include
using namespace std;
#define in
int n = 0;
int mx = 0;
int ans = 0;
int p[11] = { 0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 19 };
void dfs(int m, int ind, int num, int k){ // 当前数 当前质数编号 当前约数个数 当前指数
if(num > mx or (num == mx and m < ans)) ans = m, mx = num;
int j = 0, temp, i = m;
while(j < k){
j++;
if(n / i < p[ind]) break;
temp = num * (j + 1), i = i * p[ind];
if(i <= n) dfs(i, ind + 1, temp, j);
}
}
signed main(){
cin >> n;
dfs(1, 1, 1, 30);
cout << ans << '\n';
return 0;
}
传送门:[CQOI2007]余数求和
t a g tag tag:模运算转加减乘除???
直接推式子:
∑
i
=
1
n
k
m
o
d
i
=
∑
i
=
1
n
k
−
i
×
⌊
k
i
⌋
=
n
k
−
∑
i
=
1
n
i
×
⌊
k
i
⌋
然后我们发现后面那一坨: ∑ i = 1 n i × ⌊ k i ⌋ \sum\limits_{i = 1}^n i \times \lfloor \frac ki \rfloor i=1∑ni×⌊ik⌋ 可以整除分块,然后就可以 O ( n ) O(\sqrt n) O(n) 做完了。
#include
using namespace std;
#define int long long
int ans = 0;
int n = 0; int k = 0;
signed main(){
cin >> n >> k;
ans += n * k;
for(int l = 1, r; l <= n; l = r + 1){
if(k / l != 0) r = min(n, k / (k / l));
else r = n;
ans -= (k / l) * (l + r) * (r - l + 1) / 2;
}
cout << ans << '\n';
return 0;
}
传送门:yy 的 gcd
t a g tag tag:莫比乌斯反演???
求得答案就是( p r i m e prime prime 表示质数集):
a n s = ∑ i = 1 n ∑ j = 1 m [ gcd ( i , j ) ∈ p r i m e ] ans = \sum_{i = 1}^n \sum_{j = 1}^m [\gcd(i, j) \in prime] ans=i=1∑nj=1∑m[gcd(i,j)∈prime]
我们把他化简成能莫反的形式:
a
n
s
=
∑
k
∈
p
r
i
m
e
∑
i
=
1
n
∑
j
=
1
m
[
gcd
(
i
,
j
)
=
k
]
=
∑
k
=
∈
p
r
i
m
e
∑
i
=
1
⌊
n
k
⌋
∑
j
=
1
⌊
m
k
⌋
[
gcd
(
i
,
j
)
=
1
]
然后就是喜闻乐见的莫反环节了:
=
∑
k
=
∈
p
r
i
m
e
∑
i
=
1
⌊
n
k
⌋
∑
j
=
1
⌊
m
k
⌋
∑
d
∣
gcd
(
i
,
j
)
μ
(
d
)
再然后就是一些小 t r i c k trick trick 了:
=
∑
k
=
∈
p
r
i
m
e
∑
d
=
1
⌊
n
k
⌋
∑
i
=
1
⌊
n
k
⌋
∑
j
=
1
⌊
m
k
⌋
[
d
∣
i
∧
d
∣
j
]
μ
(
d
)
=
∑
k
=
∈
p
r
i
m
e
∑
d
=
1
⌊
n
k
⌋
⌊
n
k
d
⌋
⌊
m
k
d
⌋
μ
(
d
)
然后我们发现好像式子已经化到最简了,但是我们算一下复杂度就会发现我们愉快的 T T T 掉了。所以说这里又有一个常用的降复杂度的 t r i c k trick trick:令 T = k d T = kd T=kd,然后:
a
n
s
=
∑
k
=
∈
p
r
i
m
e
∑
d
=
1
⌊
n
k
⌋
⌊
n
T
⌋
⌊
m
T
⌋
μ
(
d
)
然后考虑枚举 T T T,并把 T T T 有关的提到前面去:
= ∑ T = 1 n ⌊ n T ⌋ ⌊ m T ⌋ ∑ k ∣ T ∧ k ∈ p r i m e μ ( T k ) = \sum_{T = 1}^n \left\lfloor \frac{n}{T} \right\rfloor \left\lfloor \frac{m}{T} \right\rfloor \sum_{k | T \land k \in prime} \mu(\frac Tk) =T=1∑n⌊Tn⌋⌊Tm⌋k∣T∧k∈prime∑μ(kT)
然后我们发现后面 ∑ k ∣ T ∧ k ∈ p r i m e μ ( T k ) \sum\limits_{k | T \land k \in prime} \mu(\frac Tk) k∣T∧k∈prime∑μ(kT) 可以预处理,具体来说对于每一个质数 k k k 枚举 k k k 的倍数 T T T,然后把她的值加上 μ ( T k ) \mu(\frac Tk) μ(kT) 就好了。
#include
using namespace std;
#define in read()
#define MAXN 10000010
inline int read(){
int x = 0; char c = getchar();
while(c < '0' or c > '9') c = getchar();
while('0' <= c and c <= '9'){
x = x * 10 + c - '0'; c = getchar();
}
return x;
}
int Q = 0;
int n = 0; int m = 0;
int cnt = 0;
int flag[MAXN] = { 0 };
int mu[MAXN] = { 0 };
int prime[MAXN] = { 0 };
long long f[MAXN] = { 0 };
long long sum[MAXN] = { 0 };
void sieve(){
mu[1] = 1;
for(int i = 2; i <= MAXN; i++){
if(!flag[i]) prime[++cnt] = i, mu[i] = -1;
for(int j = 1; j <= cnt and i * prime[j] <= MAXN; j++){
flag[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
mu[i * prime[j]] = -mu[i];
}
}
for (int i = 1; i <= cnt; i++)
for (int j = 1; prime[i] * j <= MAXN; j++)
f[j * prime[i]] += mu[j];
for (int i = 1; i <= MAXN; i++)
sum[i] = sum[i - 1] + f[i];
}
long long solve(int a,int b) {
long long ans = 0;
if (a > b) swap(a, b);
for (int l = 1, r; l <= a; l = r + 1) {
r = min(a / (a / l), b / (b / l));
ans += (sum[r] - sum[l - 1]) * (a / l) * (b / l);
}
return ans;
}
signed main(){
Q = in;
sieve();
while(Q--){
n = in; m = in;
if(n > m) swap(n, m);
cout << solve(n, m) << '\n';
}
return 0;
}
传送门:dkw 的 lcm
t a g tag tag:把数看成希尔伯特空间???
直接把式子告诉你了:
a n s = ∏ i 1 = 1 n ∏ i 2 = 1 n ⋯ ∏ i k = 1 n φ ( l c m ( i 1 , i 2 , i 3 , ⋯ , i k ) ) m o d ( 1 e 9 + 7 ) ans = \prod_{i_1 = 1}^n\prod_{i_2 = 1}^n \cdots \prod_{i_k = 1}^n \varphi(lcm(i_1, i_2, i_3, \cdots, i_k)) \mod (1e9+7) ans=i1=1∏ni2=1∏n⋯ik=1∏nφ(lcm(i1,i2,i3,⋯,ik))mod(1e9+7)
首先 t a g tag tag 里面的希尔伯特空间是个啥?简单来说无限维的空间就可以理解成一个希尔伯特空间。那么这个空间是怎么定义的呢?对于一个数 n n n 对他质因数分解,然后希尔伯特空间中的维度就代表质因子的次数。
于是每一个数字 n n n 就能被看成希尔伯特空间中的一个向量。
这样做的好处是什么呢?因为这样一来我们复杂的 l c m lcm lcm 操作就变成了对于很多的无限维向量,在每一个维度上取一个最大值,得到的新的数就是这些 n n n 维向量所代表的数的 l c m lcm lcm。
根据这个思想,我们就很容易想到把质因子单独拎出来考虑贡献(而且 φ ( n ) \varphi(n) φ(n) 还是积性函数)。所以我们直接考虑每一个 φ ( p t ) \varphi(p^t) φ(pt) 在最终的答案中出现的次数。然后这个就考虑容斥一下嘛,定义一下三个集合:
那么 φ ( p t ) \varphi(p^t) φ(pt) 出现的次数就是:
( ∣ A ∣ + ∣ B ∣ ) k − ∣ A ∣ k (|A| + |B|)^k - |A|^k (∣A∣+∣B∣)k−∣A∣k
然后我们考虑 ∣ A ∣ + ∣ B ∣ |A| + |B| ∣A∣+∣B∣ 等于多少,首先显然等于 n − ∣ C ∣ n - |C| n−∣C∣,那么我们考虑 ∣ C ∣ |C| ∣C∣ 是多少。这个也就很显然了嘛,因为 C C C 中只有 p t + 1 p^{t + 1} pt+1 的倍数,所以个数就是 ⌊ n p t + 1 ⌋ \left\lfloor\frac n{p^{t + 1}}\right\rfloor ⌊pt+1n⌋。
那么同理,我们就可以得到:
∣
A
∣
=
n
−
⌊
n
p
t
⌋
∣
B
∣
=
⌊
n
p
t
⌋
−
⌊
n
p
t
+
1
⌋
∣
C
∣
=
⌊
n
p
t
+
1
⌋
然后这里有一个细节,就是因为 ( ∣ A ∣ + ∣ B ∣ ) k − ∣ A ∣ k (|A| + |B|)^k - |A|^k (∣A∣+∣B∣)k−∣A∣k 这个东西是写在指数上的,所以根据扩展欧拉定理,运算的时候是对 1 e 9 + 6 1e9 + 6 1e9+6 取模而不是对 1 e 9 + 7 1e9 + 7 1e9+7 取模。
于是我们枚举 p p p,然后枚举 t t t,然后计算 ( n − ⌊ n p t + 1 ⌋ ) k − ( n − ⌊ n p t ⌋ ) k \left(n - \left\lfloor\frac n{p^{t + 1}}\right\rfloor\right)^k - \left(n - \left\lfloor \frac n{p^t} \right\rfloor\right)^k (n−⌊pt+1n⌋)k−(n−⌊ptn⌋)k,这个就快速幂 O ( log k ) O(\log k) O(logk) 搞定。然后前面枚举的复杂度就是质数的小于等于 n n n 的 若干次方的个数也就是 O ( n ln n ) O(\frac n{\ln n}) O(lnnn)(质数个数也是 O ( n ln n ) O(\frac n{\ln n}) O(lnnn))。于是总的复杂度就是: O ( n ln n log k ) O(\frac n{\ln n}\log k) O(lnnnlogk) 的。
t a g tag tag:还是莫反???
就直接推式子呗:
a
n
s
=
∑
i
=
1
n
l
c
m
(
i
,
n
)
=
∑
i
=
1
n
i
n
gcd
(
i
,
n
)
=
n
∑
i
=
1
n
i
gcd
(
i
,
n
)
=
n
∑
d
∣
n
∑
i
=
1
n
i
d
[
d
=
gcd
(
i
,
n
)
]
这里注意到满足 d = gcd ( i , n ) d = \gcd(i, n) d=gcd(i,n) 的 i i i 一定是 d d d 的倍数,所以这里我们考虑改为枚举 i d \frac i d di 的值:
=
n
∑
d
∣
n
∑
i
d
=
1
n
d
i
d
[
d
=
gcd
(
i
,
n
)
]
=
n
∑
d
∣
n
∑
i
=
1
n
d
i
[
d
=
gcd
(
i
d
,
n
)
]
=
n
∑
d
∣
n
∑
i
=
1
n
d
i
[
gcd
(
i
,
n
d
)
=
1
]
=
n
∑
d
∣
n
∑
i
=
1
d
i
[
gcd
(
i
,
d
)
=
1
]
这里看着就很想莫反,但是仔细思考一下发现其实不用,这里面的这一坨:
∑ i = 1 d i [ gcd ( i , d ) = 1 ] \sum_{i = 1}^d i[\gcd(i, d) = 1] i=1∑di[gcd(i,d)=1]
这个东西的意义就是 [ 1 , d ] [1, d] [1,d] 中与 d d d 互质的数字的和,那么这一坨显然就和 φ ( d ) \varphi(d) φ(d) 有关,具体怎样有关呢。
我们发现,如果 gcd ( i , d ) = 1 \gcd(i, d) = 1 gcd(i,d)=1,那么就有 gcd ( d − i , d ) = 1 \gcd(d - i, d) = 1 gcd(d−i,d)=1,所以与 d d d 互质的数成对出现并且两两和为 d d d,那么这一坨求和就显然是:
∑ i = 1 d i [ gcd ( i , d ) = 1 ] = d φ ( d ) 2 \sum_{i = 1}^d i[\gcd(i, d) = 1] = \frac{d\varphi(d)}{2} i=1∑di[gcd(i,d)=1]=2dφ(d)
那么总的式子就是:
a n s = 1 2 n ∑ d ∣ n d φ ( d ) ans = \frac 12n\sum_{d \mid n} d\varphi(d) ans=21nd∣n∑dφ(d)
显然这个函数也是积性的,所以直接线性筛筛 1 e 6 1e6 1e6 就完事了。
#include
using namespace std;
#define in read()
#define MAXN 1001000
inline int read(){
int x = 0; char c = getchar();
while(c < '0' or c > '9') c = getchar();
while('0' <= c and c <= '9'){
x = x * 10 + c - '0'; c = getchar();
}
return x;
}
int T = 0; int n = 0;
int tot = 0;
int v[MAXN] = { 0 };
int p[MAXN] = { 0 };
int phi[MAXN] = { 0 };
int ans[MAXN] = { 0 };
void sieve(int n){
phi[1] = 1;
for(int i = 2; i <= n; i++){
if(!v[i]) p[++tot] = i, phi[i] = i - 1;
for(int j = 1; j <= tot and i * p[j] <= n; j++){
v[j * p[j]] = 1;
if(i % p[j] == 0) { phi[i * p[j]] = phi[i] * p[j]; break; }
phi[i * p[j]] = phi[i] * (p[j] - 1);
}
}
for(int i = 1; i <= n; i++)
for(int j = 1; i * j <= n; j++)
ans[i * j] += j * phi[j] / 2;
for(int i = 1; i <= n; i++) ans[i] = i * ans[i] + i;
}
int main(){
T = in;
sieve(1e6);
while(T--){
n = in;
cout << ans[n] << '\n';
}
return 0;
}