牛客假日团队赛5 F- 随机数(组合数学 or 数位dp)

19 篇文章 0 订阅
15 篇文章 0 订阅

链接:https://ac.nowcoder.com/acm/contest/984/F
来源:牛客网
 

题目描述

正如你所知,奶牛们没有手指以至于不能玩“石头剪刀布”来任意地决定例如谁先挤奶的顺序。她们甚至也不能通过仍硬币的方式。
所以她们通过"round number"竞赛的方式。第一头牛选取一个整数,小于20亿。第二头牛也这样选取一个整数。如果这两个数都是 "round numbers",那么第一头牛获胜,否则第二头牛获胜。
如果一个正整数N的二进制表示中,0的个数大于或等于1的个数,那么N就被称为"round number" 。例如,整数9,二进制表示是1001,1001 有两个'0'和两个'1'; 因此,9是一个round number。26 的二进制表示是 11010 ; 由于它有2个'0'和3个'1',所以它不是round number。
很明显,奶牛们会花费很大精力去转换进制,从而确定谁是胜者。 Bessie 想要作弊,而且认为只要她能够知道在一个指定区间范围内的"round numbers"个数。
帮助她写一个程序,能够告诉她在一个闭区间中有多少Hround numbers。区间是[start, finish],包含这两个数。 (1 <= Start < Finish <= 2,000,000,000)

输入描述:

Line 1: 两个用空格分开的整数,分别表示Start 和 Finish。

输出描述:

Line 1: Start..Finish范围内round numbers的个数

示例1

输入

复制

2 12

输出

复制

6

说明

2    10  1x0 + 1x1  ROUND
3    11  0x0 + 2x1  NOT round
4   100  2x0 + 1x1  ROUND
5   101  1x0 + 2x1  NOT round
6   110  1x0 + 2x1  NOT round
7   111  0x0 + 3x1  NOT round
8  1000  3x0 + 1x1  ROUND
9  1001  2x0 + 2x1  ROUND
10  1010  2x0 + 2x1  ROUND
11  1011  1x0 + 3x1  NOT round
12  1100  2x0 + 2x1  ROUND

队友数位dp写的, 奈何我等菜鸡不会, 只能瞎猜胡写。

再更: 学了数位dp发现比组合数好写多了

组合数代码:


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll pre[110];
ll C(ll n, ll m) // 算组合数
{
	ll res1 = 1, j = 1;
	if (n / 2 < m)
		m = n - m;
	for (ll i = n - m + 1; i <= n; i++)
	{
		res1 *= i;
		while (res1 % j == 0 && j <= m)
		{
			res1 /= j;
			j++;
		}
	}
	return res1;
}
ll fun(ll a)
{
	ll u = 0, v = 0;
	ll ans = 0;
	ll x = a;
	ll tot = 0;
	while (x)
	{
		if (x & 1)
			u++;
		else
			v++;
		tot++;     // 该数字二进制位数
		x >>= 1;
	}
	if (u <= v && u) // 未排列也满足条件
		ans++;
	ll cnt1 = 1;
	ll cnt2 = 0;
	for (ll i = tot - 2; i >= 0; i--) // 枚举原数
	{
		if ((1LL << i) & a)   // 对二进制位上的1操作
		{
			cnt2++; // 把当前的1变为0
			if (i == 0)
			{
				if (cnt1 <= cnt2)
					ans++;
				continue;
			}
			for (ll j = 0; cnt1 + j <= (i - j) + cnt2; j++) // 枚举1的个数
			{
				if (i >= j)   // 后面可以排的位置要大于枚举的1的个数
					ans += C(i, j);
			}
			cnt2--;  // 再变回来
			cnt1++;  // 加上1的个数
		}
		else
			cnt2++;  // 0++
	}
	if (tot <= 1)
		return 1;
	for (ll i = 1; i < tot; i++)  // 加上低层的情况(因为低层小于当前位数, 任意排列均不会大于原数)
		ans += pre[i];
	return ans;
}
int main(){
	for (ll i = 1; i <= 32; i++)
	{
		for (ll j = 0; 1 + j <= i - 1 - j; j++) // j 是枚举 1的个数
		{
			pre[i] += C(i - 1, j);   // 打表,算每层的排列数目
		}
	}
	pre[1] = 1;
	ll a, b;
	while (cin >> a >> b)
		cout << fun(b) - fun(a - 1) << endl;
	return 0;
}

数位dp: 

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <time.h>
using namespace std;
typedef long long ll;

#ifdef LOCAL
#define debug(x) cout << "[" __FUNCTION__ ": " #x " = " << (x) << "]\n"
#define TIME cout << "RuningTime: " << clock() << "ms\n", 0
#else
#define TIME 0
#endif
#define continue(x) { x; continue; }
#define break(x) { x; break; }
const int N = 5e4 + 10;
int mod = 998244353;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
ll d[40][2][40][40];
int a[40];
int dfs(int pos, int cnt0, int cnt1, int lim, int flag) 
{
	if (pos == 0)	
		return cnt0 >= cnt1;
	if (d[pos][lim][cnt0][cnt1] != -1)
		return d[pos][lim][cnt0][cnt1];
	int up = lim ? a[pos] : 1;
	int sum = 0;
	for (int i = 0; i <= up; i++)
	{
		int u = flag;
		if (i == 1)
			u = 1;
		sum += dfs(pos - 1, cnt0 + (i == 0 && u), cnt1 + (i == 1),i == up && lim, u);
	}
	return d[pos][lim][cnt0][cnt1] = sum;
}
int get_num(int num)
{
	int cnt = 0;
	memset(d, -1, sizeof(d));
	cnt = 0;
	while (num)
	{
		a[++cnt] = num % 2;
		num /= 2;
	}
	return dfs(cnt, 0, 0, 1, 0);
}
int main(){
#ifdef LOCAL
	freopen("D:/input.txt", "r", stdin);
#endif
	int n, m;
	int num;
	while (cin >> n >> m)
		cout << get_num(m) - get_num(n - 1) << endl;
	return TIME;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值