1662: [Usaco2006 Nov]Round Numbers 圆环数

1662: [Usaco2006 Nov]Round Numbers 圆环数

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 270   Solved: 156
[ Submit][ Status][ Discuss]

Description

正如你所知,奶牛们没有手指以至于不能玩“石头剪刀布”来任意地决定例如谁先挤奶的顺序。她们甚至也不能通过仍硬币的方式。 所以她们通过"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)

Input

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

Output

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

Sample Input

2 12

Sample Output

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?大概就是当某些程序无法使用朴素算法解决的时候将其转换为2进制在2进制数下完成

对于本题,l,r超级大,O(n)算法自然超时。。。

首先本题的结果满足区间减法,设sum[i]为[0,i]的方案数,

则答案为sum[r] - sum[l-1];

设f[len][zero][one]为当前到第len位,放zero个0,one个1的合法方案数

显然这东西是可以记忆化搜索的(建立一棵二进制二叉树,可以发现相同高度的子数方案是相同的)。。。

但是有时候不能!

那什么时候可以??当且仅当当前点不在原数上且它的任意代祖宗至少有一个不是0

而对于当前位放0或放1?

如果当前位置是在原数路径上,那么能放的数取决于原数否则两个都放

 

 

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 100;

int f[maxn][maxn][maxn],l,r,num[maxn];

int dfs(int len,int zero,int one,bool root,bool line)
{
	if (!len) return zero >= one;
	if (!root && !line && f[len][zero][one] != -1) return f[len][zero][one];
	int last = line?num[len]:1;
	int tot = 0;
	for (int i = 0; i <= last; i++)
	{
		if (root)
		{
			if (!i) tot += dfs(len-1,0,0,1,line && i == num[len]);
			else tot += dfs(len-1,zero,one+1,0,line && i == num[len]);
		}
		else
		{
			if (!i) tot += dfs(len-1,zero + 1,one,0,line && i == num[len]);
			else tot += dfs(len-1,zero,one + 1,0,line && i == num[len]);
		}
	}
	if (!root && !line) f[len][zero][one] = tot;
	return tot;
}

int sum(int x)
{
	int len = 0;
	while (x)
	{
		num[++len] = x & 1;
		x >>= 1;
	}
	return dfs(len,0,0,1,1);
}

int main()
{
	cin >> l >> r;
	memset(f,-1,sizeof(f));
	cout << sum(r) - sum(l - 1);
	return 0;
}


顺便附上一发windows下的对拍。。。

 

#include<iostream>
#include<cstdio>
using namespace std;

int l,r,i,ans;

int main()
{
	cin >> l >> r;
	for (i = l; i <= r; i++)
	{
		int zero,one;
		zero = one = 0;
		for (int x = i; x; x >>= 1)
		{
			if (x & 1) ++one;
			else ++zero;
		}
		ans += (zero >= one)?1:0;
	}
	cout << ans;
	return 0;
}


 

 

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
using namespace std;

int main()
{
	srand(time(0));
	int l,r;
	r = rand() << 7;
	l = rand();
	printf("%d %d\n",l,r);
	return 0;
}


 

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<windows.h>
using namespace std;

int main()
{
	int t = 300;
	while (t--)
	{
		system("data > test.in");
		system("1662 < test.in > 1.out");
		system("baoli < test.in > 2.out");
		if (!system("fc 1.out 2.out")) break;
	}
	system("pause");
	return 0;
}


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值