2018.07.26NOIP模拟 魔法数字(数位dp)

魔法数字
题目背景
ASDFZ-NOIP2016模拟
题目描述
在数论领域中,人们研究的基础莫过于数字的整除关系。一般情况下,我们说整除总在两个数字间进行,例如 a | b(a能整除b)表示 b 除以 a 的余数为 0 。
我们称一个数字 X 是魔法的,当且仅当 X 是整数,且它能被 K 及 K 以上种一位数整除,要求这若干种一位数均在 X 的十进制表示中出现。
给出整数 K、L、R,请你计算出在区间 [L,R] 中,有多少个魔法数字。
输入格式
输入一行三个整数 K、L、R。
输出格式
输出一行一个整数,表示该区间内魔法数字的个数。
样例数据 1
输入
2 1 20
输出
2
备注
【数据范围】
对于 30% 的数据,1≤L≤R≤105;
对于 50% 的数据,1≤L≤R≤106;
对于 70% 的数据,1≤L≤R≤109;
对于 100% 的数据,1≤L≤R≤1018;0≤K≤9。

数位dpdpf[i][j][k][0/1]f[i][j][k][0/1]表示当dpdp到第ii位,当前对252252取模余数是jj,已经满足条件的数字的状态为kk(二进制压位),当前位的选取是否受到限制。

然后写一发记搜应该就能过了吧。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll f[20][253][520][2],L,R;
int stat[2520],k,len,num[20];
inline int cntsolve(int x){
    int ret=0;
    while(x)x-=x&(-x),++ret;
    return ret;
}
inline ll dfs(int pos,int mod,int sta,int lim){
    if(pos>len){
        mod-=mod/2520*2520;
        if(cntsolve(sta&stat[mod])>=k){return 1;}
        return 0;
    }
    mod-=mod/252*252;
    if(~f[pos][mod][sta][lim])return f[pos][mod][sta][lim];
    int up=lim?num[pos]:9;
    ll ret=0ll;
    for(register int i=0;i<=up;++i)ret+=dfs(pos+1,mod*10+i,(i==0)?sta:(sta|(1<<(i-1))),lim&&(i==up));
    return f[pos][mod][sta][lim]=ret;
}
inline ll solve(ll x){
    len=0,memset(f,-1,sizeof(f));
    while(x)num[++len]=x-x/10*10,x/=10;
    reverse(num+1,num+len+1);
    return dfs(1,0,0,1);
}
int main(){
    scanf("%d%lld%lld",&k,&L,&R);
    for(register int i=1;i<=9;++i)
        for(register int j=0;j<2520;j+=i)
            stat[j]|=(1<<(i-1));
    printf("%lld",solve(R)-solve(L-1));
    return 0;
}

转载于:https://www.cnblogs.com/ldxcaicai/p/9738414.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值