牛牛小数点 题解(结论+容斥)

题目链接

题目思路

感觉就是两个结论(或许打表可以发现

2 否则,x是循环的,且循环开始于小数点后第1+max⁡(p2,p5)位,其中p2表示表示质因数分解形式下2的指数项,p5​表示质因数分解下5的指数项。即f(x)=1+max⁡(p2,p5)

第一个结论,感觉好理解一点 就是可以使得分子分母进行约分,使得分母为\(10^i\)得到不循环小数

第二点的话我也不太会

然后知道结论的话设\(dp[i][j]\)表示\(1-n\)\(2^i*5^j\)的倍数有多少个

然后容斥

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=3e5+5,inf=0x3f3f3f3f,mod=998244353;
const double eps=1e-6;
ll l,r;
ll dp[100][100];
ll cal(ll n){
    memset(dp,0,sizeof(dp));
    // dp[i][j] 表示1-n里面有多少个树为2^i*5^j的倍数
    ll x=1,y=1;
    for(int i=0;i<=50;i++){
        for(int j=0;j<=25;j++){
            if(y>n) break;
            dp[i][j]=n/y;
            y=y*5;
        }
        x=x*2;
        y=x;
    }
    ll ans=0;
    for(int i=0;i<=50;i++){
        for(int j=0;j<=25;j++){
            ll num=dp[i][j]-dp[i+1][j]-dp[i][j+1]+dp[i+1][j+1]-1;
            // 减1的原因是删除只2^i*5^j
            if(num<=0) continue;
            ans=(ans+1ll*(max(i,j)+1)*num%mod)%mod;
        }
    }
    return ans;
}
signed main(){
    int _;scanf("%d",&_);
    while(_--){
        scanf("%lld%lld",&l,&r);
        ll ans=((cal(r)-cal(l-1))%mod+mod)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}


posted @ 2021-09-28 09:21  hunxuewangzi  阅读(30)  评论(0编辑  收藏  举报