题目来源:P6583 回首过去.
根据样例分析,可以
x
y
\frac xy
yx是有限小数的条件是分母只能包含因子2和5,直觉证明,整数在进行除法的过程中,如果需要去补0,则相当于*10,而10只包含因子2和5,所以出现其他因子就不是有限循环小数。
40分 n*n暴力
不解释
80分
符合条件的
x
y
\frac xy
yx可以分解为
X
Y
2
p
5
q
X
\frac{XY}{2^p5^qX}
2p5qXXY其中KaTeX parse error: Expected '}', got 'EOF' at end of input: {X%2||X%5!=0},
设
A
=
2
p
5
q
A=2^p5^q
A=2p5q,,这样我们有三个变量:在A、X、Y的个数。
且有隐含条件:AX<=n,XY<=n.
我们在A,Y确定的情况统计X的个数,A的取值范围大约为
l
o
g
2
∗
l
o
g
5
log2*log5
log2∗log5
例如
n=20,A=1,2,4,5,8,10,16,20,
根据X的取值特点,X=1,3,7,9,11,13,17,19。
可以用容斥函数计算X允许的取值情况
f
c
(
X
)
=
X
−
X
2
−
X
5
+
X
10
fc(X)=X-\frac X2-\frac X5+\frac X{10}
fc(X)=X−2X−5X+10X
举个实例来感性理解下。
A=1 时候
Y=1——》X=1…n/A;1,3,7,9,11,13,17,19
Y=2——》X=1…n/2;1,3,7,9,
Y=3——》X=1…n/3;1,3,
Y=4——》X=1…n/4;1,3,
Y=5——》X=1…n/5;1,3,
Y=6——》X=1…n/6;1,3,
Y=7——》X=1…n/7;1
…
A=2 时候
Y=1——》X=1…n/2;1,3,7,9
Y=2——》X=1…n/2;1,3,7,9,
Y=3——》X=1…n/3;1,3,
Y=4——》X=1…n/4;1,3,
Y=5——》X=1…n/5;1,3,
Y=6——》X=1…n/6;1,3,
Y=7——》X=1…n/7;1
…
设f(Y)为A,Y确定时候符合要求想X数的个数。
则
f
(
y
)
=
{
f
c
(
n
A
)
Y<=A
f
c
(
n
Y
)
Y>A
f(y)= \begin{cases} fc( \frac{n}A) & \text{Y<=A}\\ fc(\frac nY)& \text{Y>A} \end{cases}
f(y)={fc(An)fc(Yn)Y<=AY>A
对于每个确定的A ,
∑
Y
=
1
n
f
c
(
Y
)
=
A
∗
f
c
(
n
A
)
+
∑
Y
=
A
+
1
n
f
c
(
n
Y
)
\sum_{Y=1}^nfc(Y)=A*fc(\frac nA)+\sum_{Y=A+1}^nfc(\frac nY)
∑Y=1nfc(Y)=A∗fc(An)+∑Y=A+1nfc(Yn),,
∑
Y
=
A
+
1
n
f
c
(
n
Y
\sum_{Y=A+1}^nfc(\frac nY
∑Y=A+1nfc(Yn可以用一个后缀和维护。
然后枚举每个A,计算,总时间复杂度
O
(
n
∗
l
o
g
2
∗
l
o
g
5
)
O(n*log2*log5)
O(n∗log2∗log5)
//tjn,https://www.cnblogs.com/chdy/p/13009533.html
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=10010;
ll n,ans,cnt;
ll a[MAXN],s[10000009];
inline ll calc(ll x) {
return x-x/2-x/5+x/10;
}
ll work(ll x){//fy= n/t;n/y
ll w1=n/x;
ll t=n/w1,ans=0;
ans=t*calc(n/x);
ans+=s[t+1];
/* for(int i=t;i<=n;i++){
ans+=calc(n/i);
cout<<"------------"<<i<<" "<<n/i<<" calc="<<calc(n/i)<<endl;
}
cout<<x<<":"<<ans<<endl;
*/
return ans;
}
signed main() {
cin>>n;
for(ll i=1; i<=n; i=i*2)
for(ll j=1; i*j<=n; j=j*5)a[++cnt]=i*j;
sort(a+1,a+1+cnt);
for(int i=n;i>0;i--)
s[i]=s[i+1]+calc(n/i);
for(int i=1;i<=cnt;i++){
ans+=work(a[i]);
}
cout<<ans<<endl;
return 0;
}
100分做法
针对80分的做法。
面对式子
X
Y
A
X
\frac{XY}{AX}
AXXY,前面枚举了A,Y求X的个数,大量的计算用到了
n
Y
\frac nY
Yn,数据范围扩大到
1
0
12
10^{12}
1012后,已经没法存储记录了,每次枚举A的时候都要分块计算他,时间复杂
2
∗
1
0
6
2*10^6
2∗106,统计了下A的个数是137,那么总时间复杂大约度
3
∗
1
0
8
3*10^8
3∗108,没有写,运气好的话也许能拿这20分。
我们发现用这种方法进行了大量重复的计算
n
Y
\frac nY
Yn。前面分段函数Y<=A的情况也很有规律。再回到三个变量的式子A,X,Y去分析统计,可以枚举别的量吗?
如果我们枚举X,
符合要求的A的个数就是所有
A
<
=
n
X
{A<=\frac nX}
A<=Xn,随着X越来越大,数据A可以排序后统计符合要求的个数。符合要求的A会越来越少,可以单调统计。
符合要求的Y的个数就是
n
X
{\frac nX}
Xn,因此在枚举X的情况下还是
n
X
{\frac nX}
Xn的计数形式,还是可以分块。
∑
x
=
1
n
(
∑
A
=
1
n
X
∗
n
X
)
\sum_{x=1}^n(\sum _{A=1}^{\frac nX }*\frac nX)
∑x=1n(∑A=1Xn∗Xn)
然后分块计算。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=10010;
ll n,ans,cnt;
ll a[MAXN],s[10000009];
inline ll calc(ll x) {
return x-x/2-x/5+x/10;
}
ll work(ll x){//fy= n/t;n/y
ll w1=n/x;
ll t=n/w1,ans=0;
ans=(t)*calc(n/x);
ans+=s[t+1];
return ans;
}
signed main() {
cin>>n;
for(ll i=1; i<=n; i=i*2)
for(ll j=1; i*j<=n; j=j*5)a[++cnt]=i*j;
sort(a+1,a+1+cnt);
ll num=cnt;
for(ll l=1,r;l<=n;l=r+1){
r=n/(n/l);
while(a[num]>n/l&&num)num--;
//(r-l-1)*(n/l)
// cout<<"num="<<num<<"*("<<calc(r)<<"-"<<calc(l-1)<<")*"<<(n/l)<<"="<<num*(calc(r)-calc(l-1))*(n/l)<<endl;
ans+=num*(calc(r)-calc(l-1))*(n/l) ;
}
cout<<ans<<endl;
return 0;
}