对于一个式子:
∑
i
=
1
n
⌊
n
i
⌋
\sum_{i=1}^{n} \left \lfloor \frac{n}{i} \right \rfloor
i=1∑n⌊in⌋
我们容易发现在某段区间
[
l
,
r
]
[l, r]
[l,r],
⌊
n
i
⌋
\left \lfloor \frac{n}{i} \right \rfloor
⌊in⌋的值是一样的。那么我们如何获取这个区间呢。
我们假设区间左端点为
l
l
l,同时令
k
=
⌊
n
l
⌋
k = \left \lfloor \frac{n}{l} \right \rfloor
k=⌊ln⌋,
R
≥
l
R \geq l
R≥l,那么有
n
R
≥
k
\frac{n}{R} \geq k
Rn≥k
要使得 R R R最大:也就是区间右端点 r r r应该为: ⌊ n k ⌋ \left \lfloor \frac{n}{k} \right \rfloor ⌊kn⌋
最终结果应该为: r = ⌊ n ⌊ n l ⌋ ⌋ r = \left \lfloor \frac{n}{\left \lfloor \frac{n}{l} \right \rfloor} \right \rfloor r=⌊⌊ln⌋n⌋
话不多说直接看例题:
一:题目链接:The Fool
题意:
Sol:
其实打个表能发现,这个式子有规律 这是规律的AC代码
来看看分块怎么解决,我们发现
[
n
i
]
\left [ \frac{n}{i} \right ]
[in]就是
⌊
n
i
⌋
\left \lfloor \frac{n}{i} \right \rfloor
⌊in⌋
然后我们就可以利用分块算出这个值。
时间复杂度是:
O
(
N
)
O(\sqrt{N})
O(N)
Code:
int T;
cin >> T;
int tt = 1;
while(T -- )
{
int n;
cin >> n;
int res = 0;
for(int l = 1, r; l <= n; l = r+ 1)
{
if(n / l) r = min(n / (n / l), n); // 保险起见(n/l)不能为0
else r = n;
res += (n / l) * (r - l + 1);
}
if(res & 1) cout << "Case " << tt ++ << ": odd\n";
else cout << "Case " << tt ++ << ": even\n";
}
二.题目链接:Ice Rain
题意:
给出
n
n
n和
k
k
k求出下式的值:
∑
i
=
1
n
(
k
m
o
d
i
)
\sum_{i=1}^{n} ( k\bmod i)
i=1∑n(kmodi)
Sol:
我们知道:
k
m
o
d
i
=
k
−
⌊
k
i
⌋
∗
i
k \bmod i = k - \left \lfloor \frac{k}{i} \right \rfloor * i
kmodi=k−⌊ik⌋∗i
那么问题转化为:
n
∗
k
−
∑
i
=
1
n
(
i
∗
⌊
k
i
⌋
)
n * k - \sum_{i=1}^{n} (i * \left \lfloor \frac{k}{i} \right \rfloor)
n∗k−i=1∑n(i∗⌊ik⌋)
对于后面这个式子:
∑
i
=
1
n
(
i
∗
⌊
k
i
⌋
)
\sum_{i=1}^{n} (i * \left \lfloor \frac{k}{i} \right \rfloor)
i=1∑n(i∗⌊ik⌋)
我们通过分块能发现
⌊
k
i
⌋
\left \lfloor \frac{k}{i} \right \rfloor
⌊ik⌋在
[
l
,
,
r
]
[l,,r]
[l,,r]区间上值相同,那么这个区间的值就是:
(
l
+
r
)
∗
(
r
−
l
+
1
)
∗
⌊
k
i
⌋
/
2
(l+r) * (r - l + 1) * \left \lfloor \frac{k}{i} \right \rfloor / 2
(l+r)∗(r−l+1)∗⌊ik⌋/2
那么问题就迎刃而解了~~
Code:
int n, k;
while(cin >> n >> k)
{
int res = n * k;
for(int l = 1, r; l <= n; l = r + 1)
{
if(k / l ) r = min(k / (k / l), n);
else r = n;
res -= (l + r) * (r - l + 1) / 2 * (k / l);
}
cout << res << endl;
}
三. 这是链接. 模积和
题意:
Sol:
这其实就是一个推式子的题目, 其次他给的模数不是素数!!!所以求逆元要用扩展欧几里得算法来求解
来推式子:
假设
n
≤
m
n \le m
n≤m(大于的话交换就好了)
利用容斥可以得到:
∑
i
=
1
n
∑
j
=
1
m
(
n
m
o
d
i
)
∗
(
m
m
o
d
j
)
−
∑
i
=
1
n
(
n
m
o
d
i
)
∗
(
m
m
o
d
i
)
\sum_{i=1}^n \sum_{j=1}^{m}(n \bmod i)*(m \bmod j) - \sum_{i=1}^n (n \bmod i) * (m \bmod i)
i=1∑nj=1∑m(nmodi)∗(mmodj)−i=1∑n(nmodi)∗(mmodi)
同样的我们知道:
k
m
o
d
i
=
k
−
⌊
k
i
⌋
∗
i
k \bmod i = k - \left \lfloor \frac{k}{i} \right \rfloor * i
kmodi=k−⌊ik⌋∗i
所以上式可以写成:
∑
i
=
1
n
∑
j
=
1
m
(
n
−
i
∗
⌊
n
i
⌋
)
∗
(
m
−
j
∗
⌊
m
j
⌋
)
−
∑
i
=
1
n
(
n
m
−
m
i
∗
⌊
n
i
⌋
−
n
i
∗
⌊
m
i
⌋
+
i
2
∗
⌊
n
i
⌋
∗
⌊
m
i
⌋
)
\sum_{i=1}^n \sum_{j=1}^{m}(n - i * \left \lfloor \frac{n}{i} \right \rfloor)*(m - j*\left \lfloor \frac{m}{j} \right \rfloor) - \sum_{i=1}^n (nm - mi*\left \lfloor \frac{n}{i} \right \rfloor - ni*\left \lfloor \frac{m}{i} \right \rfloor + i^2 * \left \lfloor \frac{n}{i} \right \rfloor * \left \lfloor \frac{m}{i} \right \rfloor)
i=1∑nj=1∑m(n−i∗⌊in⌋)∗(m−j∗⌊jm⌋)−i=1∑n(nm−mi∗⌊in⌋−ni∗⌊im⌋+i2∗⌊in⌋∗⌊im⌋)
稍微化简一下:
∑
i
=
1
n
(
n
−
i
∗
⌊
n
i
⌋
)
∗
∑
j
=
1
m
(
m
−
j
∗
⌊
m
j
⌋
)
−
∑
i
=
1
n
(
n
m
−
m
i
∗
⌊
n
i
⌋
−
n
i
∗
⌊
m
j
⌋
+
i
2
∗
⌊
n
i
⌋
∗
⌊
m
i
⌋
)
\sum_{i=1}^n (n - i * \left \lfloor \frac{n}{i} \right \rfloor) * \sum_{j=1}^{m}(m - j*\left \lfloor \frac{m}{j} \right \rfloor) - \sum_{i=1}^n (nm - mi*\left \lfloor \frac{n}{i} \right \rfloor - ni*\left \lfloor \frac{m}{j} \right \rfloor + i^2 * \left \lfloor \frac{n}{i} \right \rfloor * \left \lfloor \frac{m}{i} \right \rfloor)
i=1∑n(n−i∗⌊in⌋)∗j=1∑m(m−j∗⌊jm⌋)−i=1∑n(nm−mi∗⌊in⌋−ni∗⌊jm⌋+i2∗⌊in⌋∗⌊im⌋)
最后化简为:
(
n
2
−
∑
i
=
1
n
(
i
∗
⌊
n
i
⌋
)
∗
(
m
2
−
∑
j
=
1
m
(
j
∗
⌊
m
j
⌋
)
−
∑
i
=
1
n
(
n
m
−
m
i
∗
⌊
n
i
⌋
−
n
i
∗
⌊
m
j
⌋
+
i
2
∗
⌊
n
i
⌋
∗
⌊
m
i
⌋
)
(n^{2}- \sum_{i=1}^n (i * \left \lfloor \frac{n}{i} \right \rfloor) * (m ^{2}-\sum_{j=1}^{m}(j*\left \lfloor \frac{m}{j} \right \rfloor) - \sum_{i=1}^n (nm - mi*\left \lfloor \frac{n}{i} \right \rfloor - ni*\left \lfloor \frac{m}{j} \right \rfloor+ i^2 * \left \lfloor \frac{n}{i} \right \rfloor * \left \lfloor \frac{m}{i} \right \rfloor)
(n2−i=1∑n(i∗⌊in⌋)∗(m2−j=1∑m(j∗⌊jm⌋)−i=1∑n(nm−mi∗⌊in⌋−ni∗⌊jm⌋+i2∗⌊in⌋∗⌊im⌋)
好了,式子推完了,用分块来求可以实现
O
(
N
)
O(\sqrt N )
O(N)。
其中:
1
2
+
2
2
+
⋅
⋅
⋅
+
n
2
=
n
∗
(
n
+
1
)
∗
(
2
n
+
1
)
6
1^2 + 2^2+···+n^2 = \frac {n*(n+1)*(2n+1)}{6}
12+22+⋅⋅⋅+n2=6n∗(n+1)∗(2n+1)
由于三个数相乘可能long long
都会爆掉,所以要用逆元。另外,此题给的模数,不是一个素数。只有当模数是素数的时候,才可以用快速幂求出
a
a
a的逆元
a
m
o
d
−
2
a^{mod-2}
amod−2。所以此题要用扩展欧几里得算法求逆元。
另外,注意取模。很容易爆掉()
Code:
#include <bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
using namespace std;
const int mod = 19940417;
const int inv2 = 9970209 ,inv6 = 3323403;
typedef long long LL;
// 1^2 + 2^2 + ··· + x^2
LL sum(int x)
{
return x * (x + 1) % mod * (2 * x + 1) % mod * inv6 % mod;
}
signed main(){
IOS;
int n, m;
cin >> n >> m;
if(n > m) swap(n, m);
int n1, m1, nm;
n1 = n * n % mod;
int l = 1, r;
for( ; l <= n; l = r + 1)
{
r = n / (n / l);
n1 = (n1 - ((r - l + 1) * (l + r) % mod * inv2 % mod * (n / l) % mod) + mod) % mod;
}
m1 = m * m % mod;
l = 1;
for( ; l <= m; l = r + 1)
{
r = m / (m / l);
m1 = (m1 - ((r - l + 1) * (l + r) % mod * inv2 % mod * (m / l) % mod) + mod) % mod;
}
l = 1;
nm = 0;
for(; l <= n; l = r + 1)
{
r = min(n / (n / l), m / (m / l));
int NM = (r - l + 1) * n % mod * m % mod;
int MI = (r - l + 1) * (l + r) % mod * inv2 % mod * m % mod * (n / l) % mod;
int NI = (r - l + 1) * (l + r) % mod * inv2 % mod * n % mod * (m / l) % mod;
int I2MN = (n / l) * (m / l) % mod * (sum(r) - sum(l - 1) + mod) % mod;
nm = ((((nm % mod + NM %mod) - MI + mod) % mod - NI + mod) % mod + I2MN + mod) % mod;
}
int ans = ((n1 * m1) % mod - nm + mod) % mod;
cout << ans << endl;
return 0;
}