例题1
求 ∑ 1 n n i , n < = 1 0 12 . \ \sum_1^n \frac{n}{i}\,,n<=10^{12}. ∑1nin,n<=1012.
题目链接.
数据范围小跑o(n)当然没问题,数据范围这么大就要另想办法。
打表观察下,设n=20
1 2 3 4 5 6 7 8 9 10 11 12 13…20
20 10 6 5 4 3 2 2 2 2 1 1 1 1
我们发现n/i的值有某些段是重复的数字。
设
n
i
=
k
\frac{n}{i}=k
in=k,
则
n
=
k
∗
i
+
d
,
0
<
=
d
<
i
n=k*i+d, 0<=d<i
n=k∗i+d,0<=d<i
那么
n
i
+
d
=
k
\frac{n}{i+d}=k
i+dn=k
i
+
d
=
n
k
=
n
n
i
i+d=\frac{n}{k}=\frac{n}{\frac{n}{i}}
i+d=kn=inn
那么i和i+d的取值就是相同的。我们找出这一段即可。
也就是我们要找出最接近n的
i
∗
k
=
n
i*k=n
i∗k=n,假设i<k,则i有
n
\sqrt n
n个,i和k共有
2
∗
n
2*\sqrt n
2∗n个。
我们设一个段的左端点为
l
l
l,则右端点
r
=
n
n
l
r=\frac{n}{\frac{n}{l}}
r=lnn.这一段的取值都为
n
l
\frac{n}{l}
ln,因此总值为:
(
r
−
l
+
1
)
∗
n
l
(r-l+1)*\frac{n}{l}
(r−l+1)∗ln
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,ans;
int main() {
cin>>n;
for(ll l=1,r; l<=n; l=r+1) {
r=n/(n/l);
ans+=(r-l+1)*(n/l);
}
cout<<ans<<endl;
}
例题2
P2261 [CQOI2007]余数求和$
求
∑
1
n
n
%
i
,
n
<
=
1
0
12
.
\ \sum_1^n n \%i\,,n<=10^{12}.
∑1nn%i,n<=1012.
n
%
i
=
n
−
n
i
∗
i
n\%i=n-\frac n i*i
n%i=n−in∗i
∑
1
n
n
%
i
=
∑
1
n
(
n
−
n
i
∗
i
)
\ \sum_1^n n \%i=\sum_1^n(n-\frac n i*i)
∑1nn%i=∑1n(n−in∗i)
=
n
∗
n
−
∑
1
n
(
n
i
∗
i
)
=n*n-\sum_1^n(\frac n i*i)
=n∗n−∑1n(in∗i)
需要求:
∑
1
n
(
n
i
∗
i
)
\sum_1^n(\frac n i*i)
1∑n(in∗i)
分段后,对每段的n/i是定值k,则这段就是ki,k(i+1)…
每段的总值为:
(
r
−
l
+
1
)
∗
n
l
∗
l
+
r
2
(r-l+1)*\frac{n}{l}*\frac{l+r}2
(r−l+1)∗ln∗2l+r
代码也放上吧。
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
int main() {
ll n,k;
scanf("%lld%lld",&n,&k);//30/8=3,,30/x=3,x=30/3=10;=>8...10为一个区间。
ll ans=n*k;
for(ll l=1,r;l<=n;l=r+1) {
if(k/l!=0) r=min(k/(k/l),n);
else r=n;
ans-=(k/l)*(r-l+1)*(l+r)/2;
}
printf("%lld",ans);
return 0;
}
P2260 [清华集训2012]模积和
这个题目懒得写了。推一堆式子。难点在于要会求
1
2
+
2
2
+
2
3
+
.
.
.
+
n
2
1^2+2^2+2^3+...+n^2
12+22+23+...+n2的通项。
查资料的得:
∑
i
=
1
n
i
2
=
n
∗
(
n
+
1
)
(
2
n
+
1
)
6
\sum_{i=1}^ni^2=\frac{n*(n+1)(2n+1)}6
∑i=1ni2=6n∗(n+1)(2n+1)
不写了,这么多公式,真麻烦。