【DP计划】10.15——[牛客网]好朋友 (数位DP) EASY+

链接:https://www.nowcoder.com/acm/contest/210/C
BLUESKY007有很多关系很好的朋友,他们无一例外,名字均由数字组成(首字符不为0)且含有"007"(例如“10007”,“10707”就是她的好朋友,而“97037”,“70709”不是),即对于字符串s存在i,j,k(i< j< k)满足

在这里插入图片描述

虽然BLUESKY007眼力极佳,一眼就能看出一个人是不是自己的好朋友,但BLUESKY007是个蒟蒻,她并不擅长数数,但她又想知道在[li,ri]内有多少人是自己的好朋友,所以就找到了你来帮忙.
她会向你询问t次,由于询问次数可能很多,所以你只需要告诉她t次询问答案的异或和即可.
输入描述:
第一行一个整数t,表示询问个数
接下来t行,每行两个整数li,ri
输出描述:
一行一个整数表示答案
示例1
输入

3
1 1000
233 666
999 999

输出

0

说明
在0~1000范围内不存在与题目要求相符的含有“007”的数,所以三次询问的答案都是0
示例2
输入

3
10000 10086
2333 23333
6666 66666

输出

132

备注:
对于20%的数据, l i ≤ r i ≤ 1 0 3 l_i≤r_i≤10^3 liri103
对于40%的数据, t ≤ 50 , l i ≤ r i ≤ 5 ⋅ 1 0 4 t≤50,l_i≤r_i≤5·10^4 t50,liri5104
对于100%的数据, 1 ≤ t ≤ 1 0 5 , 0 ≤ l i ≤ r i ≤ 1 0 18 1≤t≤10^5,0≤l_i≤r_i≤10^{18} 1t105,0liri1018

这道题的做法就是数位DP,这是显然的。
题目要求的是一个范围[l,r],所以用前缀和,把答案的形式变为calc(1,r)-calc(1,l-1),然后我们考虑怎么求答案,对于没有到达上界的,可以枚举每一位数进行转移,而如果达到上界了,就与当前的位上的数取min,然后转移即可。
这道题比较的简单,所以更多内容在代码里理解。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll T,l,r,ans,a[20],f[20][20][20][2];
ll pows(ll a,ll b){
    ll base=1;
    while(b){
        if(b&1) base=base*a;
        a=a*a;b/=2;
    }
    return base;
}
ll calc(ll p,ll lim){
    if(p==lim+1) return 1;
    ll res=0;
    for(ll i=0;i<=a[p];i++)
      if(i<a[p]) res+=pows(10,lim-p);
      else res+=calc(p+1,lim);
    return res;
}
ll dfs(ll lim,ll p,ll l,ll can){   //can为1表示没到上界,0表示到了
    if(f[lim][p][l][can]) return f[lim][p][l][can];   //用f数组记录可以优化复杂度,实际上不计会T
    if(p>lim) return 0;ll res=0;
    if(can){
        for(ll i=0;i<=9;i++){
           if(i==7){
            if(l>=2) res+=pows(10,lim-p);
            else res+=dfs(lim,p+1,l,can);
           }
           if(i==0&&p>1) res+=dfs(lim,p+1,l+1,can);
           if(i!=0&&i!=7) res+=dfs(lim,p+1,l,can);
        }
        f[lim][p][l][can]=res;
    }
    if(!can){
        for(ll i=0;i<=a[p];i++){
            if(i==7){
                if(l>=2){if(can|(i<a[p])) res+=pows(10,lim-p);else res+=calc(p+1,lim);}
                else res+=dfs(lim,p+1,l,can|(i<a[p]));
            }
            if(i==0&&p>1)
               res+=dfs(lim,p+1,l+1,can|(i<a[p]));
            if(i!=0&&i!=7)
              res+=dfs(lim,p+1,l,can|(i<a[p]));
        }
    }
    return res;
}
ll solve(ll x){
    ll p=0,t=x,res=0;
    while(t)
      p++,a[p]=t%10,t/=10;
    for(ll i=1;i*2<=p;i++) swap(a[i],a[p-i+1]);
    for(ll i=4;i<p;i++)
      res+=dfs(i,1,0,1);
    if(p>3)res+=dfs(p,1,0,0);
    return res;
}
int main()
{
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld",&l,&r);
        ans^=solve(r)-solve(l-1);
    }
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值