Good Bye 2015-------补题

A
New Year and Days
水题:

我的思路:先判断2016是闰年  2月份特判29天  整年一共366天  七个大月(31天), 四个小月(30天), 一个平月(29天), 那么对于某月的某一天, 就可以进行统计了, 对于某个月29天之内, 一共出现12次, 对于某个月第30天, 一共出现11次, 对于某个月第31天, 一共出现7次。

接下来判断周几, 首先一月一号是星期五 , 一共366天, 总共52个星期多出2天, 所以除了星期五和星期六在一年内每个出现53次, 周日到周四在一年内每个出现52次。 然后就用switch组织一下就好。

我的ac代码:

#include
using namespace std;

int main(){
	int n;
	char s[10];
	cin >> n >> s >> s; 
	if(s[0] == 'w') {
		switch(n) {
			case 5: case 6: cout << 53 << endl; break;
			default : cout << 52 << endl; break;
		}
	}
	else {
		switch(n) {
			case 31: cout << 7 << endl; break;
			case 30: cout << 11 << endl; break;
			default : cout << 12 << endl; break;
		}
	}
	return 0;
} 

官方题解两种:

The first is to hard-code numbers of days in months, check the first day of the year and then iterate over days/months —http://ideone.com/4TYPCc

The second way is to check all possible cases by hand. The 2016 consists of 52 weeks and two extra days. The answer for "x of week" will be either 52 or 53. You must also count the number of months with all 31 days and care about February. http://ideone.com/Bf9QLz


B
New Year and Old Property
我的思路: 将每一个数的二进制每一位上和1相与得到的0进行统计, 如果是一个0, 那就符合要求否则不符合要求,然而经过测试, 在第四个样例就TLE了 。。。T  T

我的TLE代码:

#include
using namespace std;
typedef long long LL;
int judge(LL n){
	int cnt = 0; 
	while(n > 0) {
		if(!(n&1)) cnt++;
		n >>= 1;
		if(cnt >= 2) return 0;
	}
	if(cnt == 1) return 1;
	else return 0;
}
int main() {
	LL a, b;
	cin >> a >> b;
	int ans = 0;
	for(LL i = a; i <= b; i++) {
		if(judge(i)) ans++;
	}
	cout << ans << endl;
	return 0;
} 

官方题解:

Each number with exactly one zero can be obtained by taking the number without any zeros (e.g. 6310 = 1111112) and subtracting some power of two, e.g. 6310 - 1610 = 1111112 - 100002 = 1011112. Subtracting a power of two changes one digit from '1' to '0' and this is what we want. But how can we iterate over numbers without any zeros? It turns out that each of them is of form 2x - 1 for some x (you can check that it's true for 6310).

What should we do to solve this problem? Iterate over possible values of x to get all possible 2x - 1 — numbers without any zeros. There are at most  values to consider because we don't care about numbers much larger than 1018. For each 2x - 1 you should iterate over powers of two and try subtracting each of them. Now you have candidates for numbers with exactly one zero. For each of them check if it is in the given interval. You can additionally change such a number into binary system and count zeros to be sure. Watch out for overflows! Use '1LL << x' instead of '1 << x' to get big powers of two. http://ideone.com/Kfu8sF

分析: 

 首先看一下数据范围:1 ≤ a ≤ b ≤ 1018, 根据题解的意思, 二进制的各位都为1的数字(2x - 1)都不会超过1018,所以最多有 个x需要考虑。

接下来要判断每一个2x - 1后再减去某一个2x‘得到的数是否出现在所给的数据范围(a, b)之间, 出现的就统计+1.

因此有两层遍历, 第一层是2x - 1, 第二层是2x‘

借鉴了一下别人的博文,这就是一个模拟二进制数的题, 不过不是把十进制转换成二进制然后每一位都进行比对(我的TLE代码就是这么写的->所以龟速), 而是在 [1, 10^18] 范围内,通过上面的规律统计出只出现二进制表示0只出现1次的数。

所以咯, 暴力。。解决。。

参考别人写的ac代码:

#include
using namespace std;
typedef long long LL;

int main() {
	LL a, b;
	cin >> a >> b;
	int ans = 0;
	for(int i = 2; i <= 61; i++) { //i到61的时候2^i就比 10^18大了 
		LL x = (1LL<= a && t <= b);
		}
	}
	cout << ans << endl;
	return 0;	
}
//对于上面代码,假设j = i-1;   i = 5 则  =>   2^i-1 = 32-1 = 31 二进制 => 11111
//此时 2^j = 2^(i-1) = 2^4 = 16 二进制 => 10000   则有  11111与10000各位对应相减 => 1111
//所以j的边界是要小于i-1的。!!! 
//要注意的小细节:数据范围是longlong型范围内  所以1左移的时候  后面加上LL
 

范围上再优化一点儿:

// One zero, by Errichto
// O(log^2(n))
#include
using namespace std;

int main() {
	long long a, b;
	scanf("%lld%lld", &a, &b);
	int c = 0;
	for(int i = 0; (1LL << i) / 2 <= b; ++i) //范围上稍微做了一点儿优化 
		for(int j = 0; j <= i - 2; ++j) {
			long long x = (1LL << i) - 1 - (1LL << j);
			c += a <= x && x <= b;
		}
	printf("%d\n", c);
	return 0;
}



你可能感兴趣的:(数据结构和算法)