分享

LuoguP6218 [USACO06NOV] Round Numbers S

 昵称71011036 2020-08-10

题目描述

如果一个正整数的二进制表示中,00的数目不小于11的数目,那么它就被称为「圆数」。

例如,99的二进制表示为10011001,其中有22个00与22个11。因此,99是一个「圆数」。

请你计算,区间[l,r][l,r]中有多少个「圆数」。

输入格式

一行,两个整数ll和rr。

输出格式

一行,一个整数,表示区间[l,r][l,r]中「圆数」的个数。

样例

输入:2 122 12         输出: 66

思路

显然这道题又是一道数位DP。但是这个题的难点和特殊之处就在于它是在二进制下处理的。这就需要我们重新揣度此题的状态。

设f[i][j][k]f[i][j][k]表示一个有ii位,且其中包括jj个11,且从右往左数第ii个数是kk的圆数的个数。这个如何转移呢?

显然的是,若j<ij<i,  f[i][j][0]=f[i−1][j][0]+f[i−1][j][1]f[i][j][0]=f[i−1][j][0]+f[i−1][j][1],若j!=0j!=0, 则f[i][j][1]=f[i−1][j−1][0]+f[i−1][j−1][1]f[i][j][1]=f[i−1][j−1][0]+f[i−1][j−1][1] (这个感性理解一下?)

最后就分成两种情况处理:

1.将位数小于cnt位的圆数累加到答案中

2.和其他数位DP类似,考虑用添数的方法解决问题。若当前遍历到该位为11,那么就存在该位为00的情况,这时候再枚举11的位数,考虑还是否能构成圆数,若能构成,累加答案即可。

最后前缀和统计答案即可。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<cstring>
  5. typedef long long ll;
  6. int l, r;
  7. ll f[35][35][2];
  8. inline int read(void){
  9. int f = 1, x = 0;char ch;
  10. do{ch = getchar();if(ch=='-')f = -1;} while (ch < '0' || ch > '9');
  11. do{ x = x * 10 + ch - '0';ch = getchar();} while (ch >= '0' && ch <= '9');
  12. return f * x;
  13. }
  14. inline void _init(void){
  15. f[1][0][0] = 1, f[1][1][1] = 1;//初始化
  16. for (int i = 2; i <= 32;++i){
  17. for (int j = 0; j <= i;++j){
  18. if(j<i) f[i][j][0] = f[i - 1][j][0] + f[i - 1][j][1];//若既有0又有1或只有0
  19. if(j) f[i][j][1] = f[i - 1][j - 1][0] + f[i - 1][j - 1][1];//若有1或全部是1
  20. }
  21. }
  22. return;
  23. }
  24. inline ll calc(int x){
  25. int bn[35], cnt = 0;
  26. ll res = 0;
  27. if(x==0) return 1;
  28. while(x){
  29. bn[++cnt] = x & 1;
  30. x >>= 1;
  31. }//二进制拆分
  32. for (int i = 1; i < cnt;++i){
  33. for (int j = 0; j <= (i >> 1); ++j)
  34. res += f[i][j][1];
  35. }//统计小于cnt位的圆数,(i>>1)这个就是保证了所得的数一定为圆数
  36. int s0 = 0, s1 = 1;//s1一定要赋值为1,因为无论如何我们讨论的数都是大于1的(为0的情况在开头就舍掉了)
  37. for (int i = cnt - 1; i >= 1;--i){
  38. if(bn[i]) for (int j = 0; j <= i;++j){
  39. if(s0+i-j>=s1+j) res += f[i][j][0];//已确定的0的个数+枚举的0的个数>=已确定的1的个数+枚举的1的个数
  40. }// s0 + i - j >= s1 + j
  41. bn[i] ? ++s1 : ++s0;
  42. }
  43. return res;
  44. }
  45. int main(){
  46. l = read(), r = read();
  47. _init();
  48. printf("%lld\n", calc(r + 1) - calc(l));
  49. return 0;
  50. }

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多