数字游戏

CSP-J2019 T1 数字游戏number

洛谷赞助题面

题解

遍历

分析

         >>
                &1

代码
#include <cstdio>
#define int unsigned char
using namespace std;

int k, cnt;

signed main() {
	for (register int i = 1; i <= 8; i++) {
		k = (k << 1) + (getchar() ^ '0');
	}

	for (register int i = 1; i <= 8; i++) {
		cnt += k & 1;
		k >>= 1;
	}

	putchar(cnt ^ '0');
	return 0;
}
证明

字符与数字之间的转换,只要一个^'0'!

十进制二进制
字符数字字符数字
4800011000000000000
4910011000100000001
5020011001000000010
5130011001100000011
5240011010000000100
5350011010100000101
5460011011000000110
5570011011100000111
5680011100000001000
5790011100100001001

我们可以发现,字符和数字之间仅相差00110000
所以,完成字符和数字之间的转换,只需要对00110000下手
而00110000=48='0'
于是就有了^'0'的操作

lowbit

分析

找1技术哪家强?树状数组lowbit!

代码
#include <cstdio>
#define int unsigned char
using namespace std;

inline int lowbit(int k) {
	return k & (~k + 1);
}
int k, cnt;

signed main() {
	for (register int i = 1; i <= 8; i++) {
		k = (k << 1) + (getchar() ^ '0');
	}

	while (k) {
		++cnt;
		k -= lowbit(k);
	}

	putchar(cnt ^ '0');
	return 0;
}
证明

这lowbit是何方神圣?

10101100

101011001
010100111
01010100

00000100

相减,不就把有1的那位消掉了吗!

归并

分析

8位并1位,试试用归并

代码
#include <cstdio>
#define int unsigned char
using namespace std;

int k;

signed main() {
	for (register int i = 1; i <= 8; i++) {
		k = (k << 1) + (getchar() ^ '0');
	}

	k = (k & 0x55) + (k >> 1 & 0x55);
	k = (k & 0x33) + (k >> 2 & 0x33);
	k = (k & 0x0f) + (k >> 4 & 0x0f);

	putchar(k ^ '0');
	return 0;
}
证明

这是什么归并?没见过呀!
>>>>>>>8
>>>4>>>4
>2>2>2>2
11111111
加以观察可以发现:

hexbit
0x5501010101
0x3300110011
0x0f00001111

分别>>1,2,4位,刚好可以将所有的1对齐
而且可以将相邻2位合并到右边
相加后,最后的1的个数就转移到最右边

取模

分析

答案不过9,骗分%个9

代码
#include <cstdio>
using namespace std;

int k;

signed main() {
	for (register int i = 1; i <= 8; i++) {
		k = (k << 3) + (k << 1) + (getchar() ^ '0');
	}

	putchar(k % 9 ^ '0');
	return 0;
}
证明

本来想骗个分,一不小心AC了。
仔细想了想,这玩意儿居然是正解!
设原数为107a+106b+105c+104d+103e+102f+101g+h ①
而我们需要的是a+b+c+d+e+f+g+h ②
由①-②得:9999999a+999999b+99999c+9999d+999e+99f+9g
所以%9即可消去不需要的东西
而答案在0~8之间,cff真强!

线段树

分析

查询区间和,启用线段树

代码
#include <cstdio>
#define unsigned char int
using namespace std;

#define MAXN 8
struct seg {
	int l, r;
	int sum;
} t[MAXN << 1];
int a[MAXN | 1];
#define li (i << 1)
#define ri (li | 1)
#define mid (l + r >> 1)
void build(int i, int l, int r) {
	t[i].l = l, t[i].r = r;
	if (l == r) {
		t[i].sum = a[l];
		return;
	}
	build(li, l, mid);
	build(ri, mid + 1, r);
	t[i].sum = t[li].sum + t[ri].sum;
	return;
}
int query(int i, int l, int r) {
	if (t[i].l == l && t[i].r == r) {
		return t[i].sum;
	}
	if (l > r) {
		return 0;
	}
	return query(li, l, mid) + query(ri, mid + 1, r);
}

signed main() {
	for (register int i = 1; i <= 8; i++) {
		a[i] = getchar() ^ '0';
	}

	build(1, 1, 8);

	putchar(query(1, 1, 8) ^ '0');
	return 0;
}
证明

我写的线段树为什么只开2倍空间?
区间长度为1,树的大小也是1
此后,区间长度每+1,就要在当前的树上搜到最浅的叶子节点,然后分开,数的大小也就+2
故,树的大小=2×区间长度-1
何苦再开4倍空间?

祝大佬CSPrp++

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值