关于二进制的规律
打完第二周的牛客萌新联赛出现了这个的板子,于是趁着机会对二进制的规律进行总结。可以和下一篇的B题结合着看,本篇也是为了让下一篇不显得太过冗余
规律总结
二进制的每一位都遵循固定周期,低位周期短,高位周期长,且呈倍数关系。通过 0-15 的二进制直观感受:
十进制 | 二进制 | 第 0 位(个位) | 第 1 位(2 位) | 第 2 位(4 位) | 第 3 位(8 位) |
---|---|---|---|---|---|
0 | 0000 | 0 | 0 | 0 | 0 |
1 | 0001 | 1 | 0 | 0 | 0 |
2 | 0010 | 0 | 1 | 0 | 0 |
3 | 0011 | 1 | 1 | 0 | 0 |
4 | 0100 | 0 | 0 | 1 | 0 |
5 | 0101 | 1 | 0 | 1 | 0 |
6 | 0110 | 0 | 1 | 1 | 0 |
7 | 0111 | 1 | 1 | 1 | 0 |
8 | 1000 | 0 | 0 | 0 | 1 |
9 | 1001 | 1 | 0 | 0 | 1 |
10 | 1010 | 0 | 1 | 0 | 1 |
11 | 1011 | 1 | 1 | 0 | 1 |
12 | 1100 | 0 | 0 | 1 | 1 |
13 | 1101 | 1 | 0 | 1 | 1 |
14 | 1110 | 0 | 1 | 1 | 1 |
15 | 1111 | 1 | 1 | 1 | 1 |
通过枚举我们可以比较直观的看到下面的规律:
- 第 k 位的周期是 2^(k+1)
- 第 0 位(1 位):周期 2(0→1 交替)
- 第 1 位(2 位):周期 4(0→0→1→1 重复)
- 第 2 位(4 位):周期 8(0→0→0→0→1→1→1→1 重复)
- 以此类推,高位周期是低位的 2 倍。
- 周期内的规律
每个周期中,前半段(2^k 个数)该位为 0,后半段(2^k 个数)为 1。
二进制位的变化具有周期性 。以第 k
位为例:
- 周期长度为
2^(k+1)
。 - 在每个周期内,前
2^k
个数的第k
位为 0 ,后2^k
个数的第k
位为 1。
例如:
- 当
k=0
(最低位)时,周期长度为2^1=2
,序列为0,1,0,1,0,1,...
。 - 当
k=1
(次低位)时,周期长度为2^2=4
,序列为0,0,1,1,0,0,1,1,...
。 - 当
k=2
(第三位)时,周期长度为2^3=8
,序列为0,0,0,0,1,1,1,1,...
。
有了上面的介绍,相信应该对二进制的规律有了直观的感受,下面有两道相关的题目。
小蓝的二进制询问
来源:D-小蓝的二进制询问_河南萌新联赛2024第(一)场:河南农业大学
思路
本题就是利用此规律求解的纯板子题
先求周期,再求余下的部分。最后将周期内的1和余下的1全部相加求解。将原来的O(n)优化到了O(60)
代码
cpp
int count(int k, int x)
{
int t=1LL<<(k+1);//计算周期
int full=(x+1)/t;//整周期的个数
int re=x+1-t*full;//计算余下个数
return (t*full/2+max(0LL, re-t/2))%mod;
//整周期中1的个数+剩余一段1的个数
//例如k=2:周期为0,0,0,0,1,1,1,1·····
//周期为8,可以观察到一个周期内前一半为0,后一半为1根据此规律进行计算
}
void solve()
{
cin >> l >> r;
int ans=0;
for(int k=0; k<=60; k++)//注意:别开大了,不然超long long了
{
ans+=count(k,r)-count(k,l-1);//将区间内在k位上1的个数相加
ans%=mod;
}
cout << ans << endl;
}
累加器
思路
这题虽然问法变了,从问统计1的个数变成了会变化多少次,其实本质都是围绕着二进制的规律展开的。
一个周期有相同个数的0和1,那么一个周期就可以分成两半。那么变化周期就是原周期的两倍。每个变化周期就会变一次,利用此规律就能很容易解决
代码
cpp
// 计算第k位在累加过程中翻转的次数
int count(int k)
{
if(k == 0) return y; // 最低位每次加1必翻转,共y次
int t = 1LL << k; //变化周期为2^k
int cnt1 = r / t; //0~r变化次数
int cnt2 = l / t; //0~l变化次数
return cnt1 - cnt2; // 区间[l, r]内的翻转次数
}
void solve()
{
cin >> x >> y;
l = x;
r = x + y;
int ans = 0;
for(int k = 0; k <= 31; k++)
ans += count(k);
cout << ans << endl;
}
好了,本篇就到此结束。下一篇的B题也是此类型,敬请期待···