文章目录
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'!
十进制 | 二进制 |
---|
字符 | 数字 | 字符 | 数字 |
---|---|---|---|
48 | 0 | 00110000 | 00000000 |
49 | 1 | 00110001 | 00000001 |
50 | 2 | 00110010 | 00000010 |
51 | 3 | 00110011 | 00000011 |
52 | 4 | 00110100 | 00000100 |
53 | 5 | 00110101 | 00000101 |
54 | 6 | 00110110 | 00000110 |
55 | 7 | 00110111 | 00000111 |
56 | 8 | 00111000 | 00001000 |
57 | 9 | 00111001 | 00001001 |
我们可以发现,字符和数字之间仅相差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
&
~10101100+1
=01010011+1
=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
加以观察可以发现:
hex | bit |
---|---|
0x55 | 01010101 |
0x33 | 00110011 |
0x0f | 00001111 |
分别>>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倍空间?