bzoj 2405: 数字 数学

题意

这里写图片描述
L,R<=10^18,T<=20

分析

通过打表我们不难发现d(n)就等于n%9.我们考虑如何查询[1,R]之间的好数。
枚举d(x),若有d(x)*x<=R,则有x<=R/d(x),且x同时要满足x%9==d(x)。那么满足条件的x数量就等于R/d(x)/9.若R/d(x)%9>=d(x)则还要+1.
但我们不难注意到这还有一个数被重复统计的情况。例如36既可以表示成6*d(6)也可以表示成12*d(12)。那么我们只要容斥掉这些情况就好了。

有大爷打表发现了答案其实是有周期性的,周期大概是两万多,那么就只要预处理前两万多的答案就可以O(1)询问了。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;

LL solve(LL R)
{
    LL ans=0;
    for (int d=1;d<=9;d++)
    {
        LL r=R/d;
        ans+=r/9;
        if (r%9>=d) ans++;
    }
    //ans-=R/18+R/8/9+(R/8%9==8)+R/14/9+(R/14%9==8);
    ans-=R/54+(R%54>=36)+R/180+(R%180>=160)+R/126+(R%126>=112)+R/72+(R%72>=64);
    return ans;
}

int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        LL l,r;
        scanf("%lld%lld",&l,&r);
        printf("%lld\n",solve(r)-solve(l-1));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值