给出两个结论
- 对
进行质因数分解,若只含有质因子
和
,则
是不循环小数,即
- 否则,
是循环的,且循环开始于小数点后第
位,其中,
表示表示质因数分解形式下
的指数项,
表示质因数分解下
的指数项。即
首先,类似前缀和的思路,只要能求解~
的答案,相减就可以得到
的答案。下面,我们考虑一种类似容斥的做法,记
表示
~
中
的倍数有多少个。初始时
,然后我们按照
的值从大到小遍历,每遍历到一组
,就对所有的
进行一次
。这样,当继续往下遍历的时候,就能保证
总是能“恰好”被
整除的数的个数(而不会被更大的数整除)。在遍历的时候顺便统计答案即可。
总复杂度,足够通过本题(尽管代入左式算出来会有
的运算量,但由于上式复杂度估的很粗,偏大,实际上跑得很快)。
当然也可以使用二维树状数组继续优化,达到更优的复杂度。
结论二略证:首先,若无
因子,由欧拉降幂,
一定有解(由欧拉降幂),换言之,存在一个解
和一个
使得
,因此我们可以通分:
,则可以得出这是一个循环节长度为
,循环节内容为
的小数。例如
,至于为什么一个数除以形如
的数字会得到循环节为分子的无限循环小数,这个难以言传,但实际写一下竖式运算就会发现这个结论很对?总之,综上,若
无
因子,由上述推导,一定有循环且一定在小数点后1位开始,此时有
。
而对于有因子的情况,我们乘以
个10可以得到一个新的小数(这个小数分子可能不为1,但这没有影响,因为上一段的推导可以看出,分子只影响循环节的内容,不影响长度和开始位置),而此时,我们得到的小数是从小数点后第1位开始循环的,因此原数
的
。
#include<bits/stdc++.h> using namespace std; #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,a,n) for(int i=n;i>=a;i--) #define pb push_back #define SZ(v) ((int)v.size()) #define fs first #define sc second typedef long long ll; typedef double db; typedef pair<int,int> pii; int T; const ll inf=1e15,mod=998244353; ll l,r,cnt[55][32],num[55][32]; ll solve(ll x){ memset(cnt,0,sizeof(cnt)); rep(i,0,50){ rep(j,0,25){ if(num[i][j]) cnt[i][j]=x/num[i][j]; } } ll ret=0; per(k,0,75){ rep(i,0,min(k,50)){ int j=k-i; if(j>25) continue; if(cnt[i][j]){ ret+=((ll)(max(i,j)+1)*(cnt[i][j]-1)); rep(ii,0,i){ rep(jj,0,j){ cnt[ii][jj]-=cnt[i][j]; } } } } } return ret; } int main(){ rep(i,0,50){ if(!i) num[i][0]=1; else num[i][0]=2ll*num[i-1][0]; rep(j,1,25){ num[i][j]=num[i][j-1]*5ll; if(num[i][j]>inf){ break; } } } cin>>T; while(T--){ scanf("%lld%lld",&l,&r); assert(l<=r&&l>0&&r>0&&r<=inf); printf("%lld\n",(solve(r)-solve(l-1))%mod); } return 0; }