前言:
断更太久,已经忘了博客怎么写了。。
这篇博客用来介绍带整除式子的快速求解办法。
符号说明:
⌊
a
b
⌋
\left \lfloor \frac{a}{b}\right \rfloor
⌊ba⌋ :
a
a
a除以
b
b
b向下取整。
⌈
a
b
⌉
\left \lceil \frac{a}{b}\right \rceil
⌈ba⌉ :
a
a
a除以
b
b
b向上取整。
∑
i
=
1
n
f
(
i
)
\sum_{i=1}^{n}f(i)
∑i=1nf(i) : 累加符号,表示
f
(
1
)
+
f
(
2
)
+
…
+
f
(
n
)
f(1)+f(2)+…+f(n)
f(1)+f(2)+…+f(n)。
问题:
求解
∑
i
=
1
n
⌊
n
i
⌋
\sum_{i=1}^{n}\left \lfloor \frac{n}{i}\right \rfloor
∑i=1n⌊in⌋,其中
1
⩽
n
⩽
1
e
12
1\leqslant n \leqslant 1e12
1⩽n⩽1e12。
正文:
首先,不难发现有一个o(n)的暴力。
我们需要一个
n
\sqrt{n}
n的做法。
我们可以先举例观察,我们举
n
=
10
n=10
n=10,然后枚举从
1
1
1到
n
n
n枚举
i
i
i,观察
⌊
n
i
⌋
\left \lfloor \frac{n}{i}\right \rfloor
⌊in⌋的值。
1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10
10 10 10 5 5 5 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
就是上面这个样子。
不难发现也不难证明,相同的数都是连续的,可以把它分成一块一块值相同的段。那么我们可以利用这个性质,对于每一块,将这个块的数字乘上块的长度,就可以求解这一块的和了。
现在还有两个问题要解决。
一个是块的数量,一个是如何快速找到块的两端。
首先块的数量,对于
i
i
i从
1
1
1到
n
n
n取值,
⌊
n
i
⌋
\left \lfloor \frac{n}{i}\right \rfloor
⌊in⌋的值的种数不会超过
2
n
2\sqrt{n}
2n,也就是块的数量不会超过
2
n
2\sqrt{n}
2n。
证明其实很容易,
嗯,可以证,前
n
\sqrt{n}
n个
i
i
i最多有
n
\sqrt{n}
n种值,而当
i
i
i大于
n
\sqrt{n}
n时,取值一定小于
n
\sqrt{n}
n,所以不超过
2
n
2\sqrt{n}
2n(摘自ztc口糊)。
那么第二个问题,快速找到块的端点。
当某一块的左端点为
l
l
l,那么它的右端点
r
r
r就是
⌊
n
⌊
n
l
⌋
⌋
\left \lfloor \frac{n}{\left \lfloor \frac{n}{l}\right \rfloor}\right \rfloor
⌊⌊ln⌋n⌋。 这个就更好证了。
也可以理解成,已知
i
i
i,那么与
i
i
i相同的块的右端点就是
⌊
n
⌊
n
i
⌋
⌋
\left \lfloor \frac{n}{\left \lfloor \frac{n}{i}\right \rfloor}\right \rfloor
⌊⌊in⌋n⌋
也就是说,当
i
i
i的取值在
l
l
l到
r
r
r之间时,式子的结果是一样的,那么只要再乘上区间长度
(
r
−
l
+
1
)
(r-l+1)
(r−l+1)即可。
最后再对于原问题,上模板代码:(有取模的加取模)
ll ans=0;
for(ll l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans+=(r-l+1)*(n/l);
}
例题及应用:
再拓展几个例题,有,但不全,还是要去多找题做。
问题1:
已知 w w w和 n n n,求解 ∑ i = 1 n ⌊ w i ⌋ \sum_{i=1}^{n}\left \lfloor \frac{w}{i}\right \rfloor ∑i=1n⌊iw⌋,其中 1 ⩽ n , w ⩽ 1 e 12 1\leqslant n,w \leqslant 1e12 1⩽n,w⩽1e12。
解:
与原题不同,这里的枚举上界
n
n
n不再作为被除数。
那么在计算右端点
r
r
r的时候,会引发一个细节问题。
看式子,
r
=
w
/
(
w
/
l
)
r=w/(w/l)
r=w/(w/l)。会出现两个种特殊情况。
一种是
w
/
l
=
=
0
w/l==0
w/l==0,此时再调用这个式子就会出现除
0
0
0的情况,导致RE。
还有一种是,计算结果
r
>
n
r>n
r>n,此时右端点不在边界内了。
要把这两种情况考虑进去,在原代码的基础上进行修改。
ll ans=0;
for(ll l=1,r;l<=n;l=r+1)
{
if(w/l)r=w/(w/l);
else r=n;
r=min(r,n);
ans+=(r-l+1)*(w/l);
}
问题2:
已知 a , b a,b a,b和 n n n,求解 ∑ i = 1 n ⌊ a i ⌋ ⌊ b i ⌋ \sum_{i=1}^{n}\left \lfloor \frac{a}{i}\right \rfloor\left \lfloor \frac{b}{i}\right \rfloor ∑i=1n⌊ia⌋⌊ib⌋,其中 1 ⩽ n , a , b ⩽ 1 e 12 1\leqslant n,a,b \leqslant 1e12 1⩽n,a,b⩽1e12。
解:
在原问题的基础上多乘了一个,不过别怕,只不过从原来
2
a
2\sqrt{a}
2a个断点,变成了
2
a
+
2
b
2\sqrt{a}+2\sqrt{b}
2a+2b个断点,复杂度仍然是根号级别的。
我们只要每次找最近的那个断点即可。
ll ans=0;
for(ll l=1,r,r1,r2;l<=n;l=r+1)
{
if(a/l)r1=a/(a/l);
else r1=n;
if(b/l)r2=b/(b/l);
else r2=n;
r=min(r1,r2)
r=min(r,n);
ans+=(r-l+1)*(a/l)*(b/l);
}
问题3:
已知 w w w和 n n n,求解 ∑ i = 1 n ⌈ w i ⌉ \sum_{i=1}^{n}\left \lceil \frac{w}{i}\right \rceil ∑i=1n⌈iw⌉,其中 1 ⩽ n , w ⩽ 1 e 12 1\leqslant n,w \leqslant 1e12 1⩽n,w⩽1e12。
解:
这是向上取整的,不好找到想原来一样的结论,但是我们可以把向上取整转化为向下取整。
⌈
w
i
⌉
=
⌊
w
+
i
−
1
i
⌋
=
⌊
w
−
1
i
⌋
+
1
\left \lceil \frac{w}{i}\right \rceil=\left \lfloor \frac{w+i-1}{i}\right \rfloor=\left \lfloor \frac{w-1}{i}\right \rfloor+1
⌈iw⌉=⌊iw+i−1⌋=⌊iw−1⌋+1
就可以轻松解决了。
ll ans=0;
for(ll l=1,r;l<=n;l=r+1)
{
if((w-1)/l)r=(w-1)/((w-1)/l);
else r=n;
r=min(r,n);
ans+=(r-l+1)*((w-1)/l+1);
}
问题5:
已知 w w w和 n n n,求解 ∑ i = 1 n i ⌊ w i ⌋ \sum_{i=1}^{n}i\left \lfloor \frac{w}{i}\right \rfloor ∑i=1ni⌊iw⌋,其中 1 ⩽ n , w ⩽ 1 e 12 1\leqslant n,w \leqslant 1e12 1⩽n,w⩽1e12。
解:
多乘了一个 i i i,与原来不同的就是,这次我们不能再乘 r − l + 1 r-l+1 r−l+1了,前面 r − l + 1 r-l+1 r−l+1的本质其实是那一段的区间和,这时候我们要乘上的是区间 i i i的求和。(因为这段区间里 ⌊ w i ⌋ \left \lfloor \frac{w}{i}\right \rfloor ⌊iw⌋是一样的,相当于一个常系数,所以才可以这么乘的)
ll ans=0;
for(ll l=1,r;l<=n;l=r+1)
{
if(w/l)r=w/(w/l);
else r=n;
r=min(r,n);
ans+=(r-l+1)*(l+r)/2*(w/l);
}
问题6:模和积
求
∑
i
=
1
n
∑
j
=
1
m
(
n
m
o
d
i
)
×
(
m
m
o
d
j
)
,
i
≠
j
\sum_{i=1}^{n}\sum_{j=1}^{m}(n\;mod\;i)\times (m\;mod\;j),i\neq j
∑i=1n∑j=1m(nmodi)×(mmodj),i=j模
19940417
19940417
19940417的值。
其中
1
⩽
n
,
m
⩽
1
e
9
1\leqslant n,m \leqslant 1e9
1⩽n,m⩽1e9。
解:
首先要知道
(
n
m
o
d
i
)
=
n
−
i
×
⌊
w
i
⌋
(n\;mod\;i)=n-i\times \left \lfloor \frac{w}{i}\right \rfloor
(nmodi)=n−i×⌊iw⌋。
然后开始化式子,注意
i
≠
j
i\neq j
i=j的条件不能忽略。
化好公式后,就可以写了。
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
typedef long long ll;
const ll mod=19940417;
typedef pair<int,int> P;
const ll MAXN=1000000;
const ll inv6=3323403;
ll sum(ll l,ll r,ll flag)
{
if(flag==1)
{
return (r-l+1)*(l+r)/2%mod;
}
else
{
return (r*(r+1)%mod*(2*r+1)%mod*inv6%mod-(l-1)*l%mod*(2*l-1)%mod*inv6%mod+mod)%mod;
}
}
int main()
{
ll n,m;
scanf("%lld%lld",&n,&m);
ll sum1=0;
for(ll l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
sum1=(sum1+n*(r-l+1)%mod-sum(l,r,1)*(n/l)%mod+mod)%mod;
}
ll sum2=0;
for(ll l=1,r;l<=m;l=r+1)
{
r=m/(m/l);
sum2=(sum2+m*(r-l+1)%mod-sum(l,r,1)*(m/l)%mod+mod)%mod;
}
ll nm=min(n,m);
ll sum3=0;
for(ll l=1,r,r1,r2;l<=nm;l=r+1)
{
if(n/l)r1=n/(n/l);
else r1=nm;
if(m/l)r2=m/(m/l);
else r2=nm;
r=min(r1,r2);
r=min(r,nm);
sum3=(sum3+n*m%mod*(r-l+1)%mod-(n*(m/l)%mod+m*(n/l)%mod)*sum(l,r,1)%mod+sum(l,r,2)*(n/l)%mod*(m/l)%mod+mod)%mod;
}
ll ans=(sum1*sum2-sum3+mod)%mod;
printf("%lld\n",ans);
return 0;
}
问题7:余数求和
解:
问题6的简化版本,就不贴代码了。