Round Numbers(poj3252)(组合数学||数位DP)

原题目:

Description

The cows, as you know, have no fingers or thumbs and thus are unable to play Scissors, Paper, Stone' (also known as 'Rock, Paper, Scissors', 'Ro, Sham, Bo', and a host of other names) in order to make arbitrary decisions such as who gets to be milked first. They can't even flip a coin because it's so hard to toss using hooves.

They have thus resorted to "round number" matching. The first cow picks an integer less than two billion. The second cow does the same. If the numbers are both "round numbers", the first cow wins,
otherwise the second cow wins.

A positive integer N is said to be a "round number" if the binary representation of N has as many or more zeroes than it has ones. For example, the integer 9, when written in binary form, is 1001. 1001 has two zeroes and two ones; thus, 9 is a round number. The integer 26 is 11010 in binary; since it has two zeroes and three ones, it is not a round number.

Obviously, it takes cows a while to convert numbers to binary, so the winner takes a while to determine. Bessie wants to cheat and thinks she can do that if she knows how many "round numbers" are in a given range.

Help her by writing a program that tells how many round numbers appear in the inclusive range given by the input (1 ≤ Start < Finish ≤ 2,000,000,000).

Input

Line 1: Two space-separated integers, respectively Start and Finish.

Output

Line 1: A single integer that is the count of round numbers in the inclusive range Start..Finish

Sample Input

2 12

Sample Output

6

中文概要:
 你知道的牛没有手指或拇指,因此无法玩石头剪刀布,以便做出任意决定,比如谁首先被挤奶。他们甚至不能翻转硬币,因为很难用蹄子折腾。 
    因此,他们采取了"Round Numbers"配对。第一只牛选择一个小于20亿的整数,第二只牛做同样的事情。如果数字都是“Round Number”,那么第一头牛就会赢,
否则第二头牛胜。 
如果N的二进制表示中0的个数大于或等于1的个数,则正整数N被称为“Round Number”。例如,当以二进制形式写入时,整数9是1001,1001有两个0和两个1;因此,9是一个"Round Number"。整数26是二进制的11010;由于它有两个0和三个1,它不是一个"Round Number"。
显然,奶牛需要一段时间才能将数字转换为二进制,所以获胜者需要一段时间才能确定。Bessie想要作弊,如果她知道在一定范围内有多少“Round Number”,她就可以作弊。
帮助她编写一个程序,告诉输入中给出的包含范围内有多少个“Round Number”(1≤Start≤Finish≤2,000,000,000)。 
 

 

组合数学法(没看懂诶):

题解:
可以设定一个get_prefix(int n)的函数,也就是求出从1---->n中有多少个二进制0的个数大于等于1的个数的合
法数字,然后减一下就可以。
具体求的方法是,把n转换为2进制,假设为n的二进制一共有len这么长,先不考虑n的二进制最高位,即只考虑剩下的
len-1位,当长度为len-1时,最高位为1,剩下的便是一个计数组合问题了,放多少个0是合法的,即1XXXXX....
具体的例子借鉴一下Bin神题解的例子是22
拆为二进制为 22 = 1 0 1 1 0  len = 5
去除最高位的1,
当len = 4的时候,最高位为1,即1XXX,有三个位置可以放,可以放0也可以放1,合法的放法是2个0或3个0(算上最
高位的1),即有C(3,2) + C(3,3) = 4
同理,len = 3的时候,最高位为1,即1XX,有两个位置可以放,有C(2,2) = 1
同理,len = 2的时候,有C(1,1) = 1
剩下要解决的便是len = 5的时候的,要根据n的二进制去遍历n的二进制数组,当较高位为0的时候可以略过,担当最高
位为1的时候就可以把1换为0了.
 

#include <algorithm>
#include <iostream>
#include <numeric>
#include <cstring>
#include <iomanip>
#include <string>
#include <vector>
#include <cstdio>
#include <queue>
#include <stack>
#include <cmath>
#include <map>
#include <set>
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const LL MAX = 405;
const double esp = 1e-6;
const double PI = 3.1415926535898;
const int INF = 0x3f3f3f3f;
using namespace std;
LL dp[40][40];
void init(){
    memset(dp,0,sizeof(dp));
    for(int i=0;i<=35;i++)
        dp[i][i] = dp[0][i] = dp[1][i] = 1;
    for(int i=0;i<=35;i++)
        dp[i][0] = 1;
    for(int i=1;i<=35;i++){
        for(int j=1;j<=35;j++){
            if(i != j)
                dp[i][j] = dp[i-1][j] + dp[i-1][j-1];
        }
    }
}
LL cal(LL n){
    if(n <= 1)
        return 0;
    LL two[40],t = 0,m = n,zero = 0,one = 0;
    while(m){
        two[t++] = m%2;
        if(m%2)
            one += 1;
        else
            zero += 1;
        m /= 2;
    }
    LL ans = 0;
    for(int i = t - 1;i > 0;i--){
        if(i%2)
            ans += ((1<<(i-1)) - dp[i-1][(i-1)/2]) / 2;
        else
            ans += ((1<<(i-1))) / 2;
    }
    if(zero >= one)
        ans += 1;
    zero = 0;one = 1;
    for(int i =  t - 2;i >= 0;i--){
        if(two[i] == 0)
            zero += 1;
        else{
            for(int j=i;j >= 0 && j + zero + 1 >= i - j + one;j--)
                ans += dp[i][j];
            one += 1;
        }
    }
    return ans;
}
int main(){
    LL n,m;
    init();
    while(~scanf("%lld %lld",&n,&m)){
        printf("%lld\n",cal(m) - cal(n - 1));
    }
    return 0;
}

数位DP(还是没看懂):

#include<iostream>
#include<sstream>
#include<iterator>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<vector>
#include<bitset>
#include<climits>
#include<queue>
#include<iomanip>
#include<cmath>
#include<stack>
#include<map>
#include<ctime>
#include<new>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define MT(a,b) memset(a,b,sizeof(a))
#define lson l, mid, node << 1
#define rson mid + 1, r, node << 1 | 1
const int INF  =  0x3f3f3f3f;
const int O    =  1e6;
const int mod  =  10007;
const int maxn =  1e4+5;
const double PI  =  acos(-1.0);
const double E   =  2.718281828459;
const int _ = 1;
const int __ = 2;
const int ___ = 3;
int dp[35][35][35]; // dp[pos][num0][num1]表示pos为0和1的个数已经为num0和num1,满足条件的个数
int a[35];
int dfs(int pos, int num0, int num1, int lead, int flag){
    if(pos == -1) return num0 >= num1;
    if(!flag && !lead && dp[pos][num0][num1] != -1) return dp[pos][num0][num1];
    int sum = 0;
    int up =  flag ? a[pos] : 1;
    for(int i=0; i<=up; i++){
        sum += dfs(pos-1, (!i && lead) ? 0 : num0 + !i, (!i && lead) ? 0 : num1 + i, !i && lead, flag && i==a[pos]);

//        或者:

//        if(i == 0 && lead) sum += dfs(pos -1, 0, 0, lead && i == 0, flag && i == a[pos]);

//        else if(i !=0 && lead) sum += dfs(pos -1, 0, 1, lead && i == 0, flag && i == a[pos]);

//        else if(i == 0 && !lead) sum += dfs(pos-1, num0+1, num1, lead && i == 0, flag && i == a[pos]);

//        else if(i !=0 && !lead) sum += dfs(pos-1, num0, num1 +1, lead && i == 0, flag && i == a[pos]);
    }
    if(!flag && !lead) dp[pos][num0][num1] = sum;
    return sum;
}
int solve(int x){
    int cnt = 0;
    while(x) { a[cnt ++] = x & 1; x >>= 1; }
    return dfs(cnt -1, 0, 0, 1, 1);
}
int main(){
    int l, r; scanf("%d%d", &l, &r);
    MT(dp, -1);
    printf("%d\n", solve(r) - solve(l-1));
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

deebcjrb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值