先%SY...

课件链接

求1的个数

以32位整数为例子,最暴力的方法就是一位一位的数,但是这样太不优美...

以下是优美的方法...

这个问题其实就是二进制求和...

我们考虑分治的思想...每一次都把当前数字分成两部分,把通过二进制移位来实现两部分数字的相加...这样就可以达到$O(log_{2}N)$的复杂度...

No.1

以2bit为一段,取出所有奇数位,右移一位,和偶数位相加...

No.2

以4bit为一段,取出所有第奇数位,右移两位,和第偶数位相加...

......

inline int pro1(u32 x){
x=((x&0xAAAAAAAAu)>>1 )+(x&0x55555555u);
x=((x&0xCCCCCCCCu)>>2 )+(x&0x33333333u);
x=((x&0xF0F0F0F0u)>>4 )+(x&0x0F0F0F0Fu);
x=((x&0xFF00FF00u)>>8 )+(x&0x00FF00FFu);
x=((x&0xFFFF0000u)>>16)+(x&0x0000FFFFu);
return x;
}

但是还是可以优化...

inline int pro2(u32 x){
x-=((x&0xAAAAAAAAu)>>1);
x=((x&0xCCCCCCCCu)>>2)+(x&0x33333333u);
x=((x>>4)+x)&0x0F0F0F0Fu;
x=((x>>8)+x)&0x00FF00FFu;
x=((x>>16)+x)&0x0000FFFFu;
return x;
}

首先看第一行:

x=(x&0xAAAAAAAAu)+(x&0x55555555u);

y=((x&0xAAAAAAAAu)>>1)+(x&0x55555555u);

x-y=(x&0xAAAAAAAAu)>>1;

y=x-(x&0xAAAAAAAAu)>>1;

然后这样奇数偶数位已经对齐,第二行不变...

第三行:直接将整个数取出,右移相加,然后只区有用的那些位置...

但是大神们还是觉得这样不够优美,所以接着优化...

inline int pro3(u32 x){
x-=((x&0xAAAAAAAAu)>>1);
x=((x&0xCCCCCCCCu)>>2)+(x&0x33333333u);
x=((x>>4)+x)&0x0F0F0F0Fu;
x=(x*0x01010101u)>>24;
return x;
}

最后一行是毛线??

我们先来看一看0x01010101是个什么东西...

转化成二进制之后这个数的第1位第8位第16位和第24位是1,其他位都是0...

我们看x在经历了前三次计算之后是什么样子的:x=(a<<24)+(b<<16)+(c<<8)+(d<<0)...a+b+c+d就是我们想要的结果...

那么好了,x*0x01010101之后是什么?

x=

((a)<<48)+
((a+b)<<40)+
((a+b+c)<<32)+
((a+b+c+d)<<24)+
((b+c+d)<<16)+
((c+d)<<8)+
((d)<<0)

前三个直接溢出...后三个>>24之后变成0...所以x>>24=a+b+c+d...

这样我们就可以快速求出单个数的1的个数...

如果有很多询问呢?我们是不是可以打表处理呢?

对于预处理操作最直观的想法是递推...f[0]=0,f[i]=f[i>>1]+i&1...

但是貌似表有点大...

所以考虑分段的思想...预处理出所有16位整数的答案,然后每一次查询拆成前16位和后16位的答案和...单次查询复杂度稳稳的$O(2)$...

inline void prework(void){
f[0]=0;
for(int i=1;i<(1<<16);i++)
f[i]=f[i>>1]+(i&1);
} inline int pro4(u32 x){
int lala=x>>16;
return f[lala]+f[x^((lala<<16))];
}

求1的个数的奇偶性

最简单直接的方法就是计算出1的个数然后判断奇偶性...但是太不优美了...

还是考虑分治的思想...每一次把数字分成两部分,这两部分^一下不改变奇偶性,所以我们就不断缩小位数直到只有1位...

inline int pro(u32 x){
x^=x>>16;
x^=x>>8;
x^=x>>4;
x^=x>>2;
x^=x>>1;
return x&1;
}

对于很多次查询的问题我们还是可以打表预处理...

inline void prework(void){
f[0]=0;
for(int i=1;i<(1<<16);i++)
f[i]=f[i>>1]^(i&1);
} inline int pro2(u32 x){
int lala=x>>16;
return f[lala]^f[x^(lala<<16)];
}

其实以上两个问题GCC都有内建函数...但是比赛请慎重...

翻转位序

还是以32位整数为例...

翻转位序就是第0位与第31位交换,第1位与第30位交 换,……第i位与第31-i位交换,……第15位与第16位交换...

还是分治的思想...

将数分为两个部分,两个部分分别翻转,然后交换位置...

inline u32 pro1(u32 x){
x=((x&0xAAAAAAAAu)>>1 )|((x&0x55555555u)<<1 );
x=((x&0xCCCCCCCCu)>>2 )|((x&0x33333333u)<<2 );
x=((x&0xF0F0F0F0u)>>4 )|((x&0x0F0F0F0Fu)<<4 );
x=((x&0xFF00FF00u)>>8 )|((x&0x00FF00FFu)<<8 );
x=((x&0xFFFF0000u)>>16)|((x&0x0000FFFFu)<<16);
return x;
}

对于多次询问我们依旧采用查表的方法...把数拆成两个部分,预处理所有16位整数的答案,然后计算...

inline void prework(void){
f[0]=0;
for(int i=1;i<(1<<16);i++)
f[i]=(f[i>>1]>>1)|((i&1)<<15);
} inline u32 pro2(u32 x){
int lala=x>>16;
return (f[x^(lala<<16)]<<16)|f[lala];
}

求前缀0/后缀0的个数

二分查找...

inline int LeadingZeroCount(u32 x){
int ans=0;
if(x>>16) x>>=16; else ans|=16;
if(x>>8 ) x>>=8 ; else ans|=8 ;
if(x>>4 ) x>>=4 ; else ans|=4 ;
if(x>>2 ) x>>=2 ; else ans|=2 ;
if(x>>1 ) x>>=1 ; else ans|=1 ;
ans+=!x;
return ans;
} inline int TrailingZeroCount(u32 x){
int ans=0;
if(!(x&((1<<16)-1))) x>>=16,ans|=16;
if(!(x&((1<<8 )-1))) x>>=8 ,ans|=8 ;
if(!(x&((1<<4 )-1))) x>>=4 ,ans|=4 ;
if(!(x&((1<<2 )-1))) x>>=2 ,ans|=2 ;
if(!(x&((1<<1 )-1))) x>>=1 ,ans|=1 ;
ans+=!x;
return ans;
}

然后此题依旧可以查表解决...判断第一个1出现在前一段还是后一段,然后查询...

(我打赌这绝对是我起过的最长的函数名QAQ...)

inline void LeadingZeroCountPrework(void){
fl[0]=16;
for(int i=1;i<(1<<16);i++)
fl[i]=fl[i>>1]-1;
} inline void TrailingZeroCountPrework(void){
ft[0]=16;
for(int i=1;i<(1<<16);i++){
if(i&1)
ft[i]=0;
else
ft[i]=ft[i>>1]+1;
}
} inline int LeadingZeroCount2(u32 x){
int lala=x>>16;
if(lala)
return fl[lala];
return fl[x^(lala<<16)]+16;
} inline int TrailingZeroCount2(u32 x){
int lala=x>>16;
if((x^(lala<<16))>0)
return ft[x^(lala<<16)];
return ft[lala]+16;
}

依旧有内建函数...但是要慎重...

求第k个1的位置

依旧是二分的思想...

求第k个1的位置可以转化为求最大的pos使得第0位到第pos-1位中1的个数等于k-1...

我们之前已经有了快速求解一个数中1的个数的方法,可以直接拿来用...

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
#define u32 unsigned int
using namespace std; const int maxn=(1<<16)+5; int f[maxn]; inline void prework(void){
f[0]=0;
for(int i=1;i<(1<<16);i++)
f[i]=f[i>>1]+(i&1);
} inline int pro(u32 x,int k){
int ans=0;
if(f[x&65535u]<k)
k-=f[x&65535u],ans|=16,x>>=16;
if(f[x&255u ]<k)
k-=f[x&255u ],ans|=8 ,x>>=8 ;
if(f[x&15u ]<k)
k-=f[x&15u ],ans|=4 ,x>>=4 ;
if(f[x&3u ]<k)
k-=f[x&3u ],ans|=2 ,x>>=2 ;
if(f[x&1u ]<k)
k-=f[x&1u ],ans|=1 ,x>>=1 ;
return ans;
} signed main(void){
u32 x=12819;prework();
cout<<pro(x,4)<<endl;
return 0;
}//Cap ou pas cap. Cap.

提取末尾连续的1

因为1是连续的,所以我们把x+1之后,右边连续的1会变成连续的0,最右边的0会变成1,其他位不变...

inline u32 pro(u32 x){
return x&(x^(x+1));
}

集合:

集合的表示

把一个集合映射到[0,n-1],用二进制整数表示集合中元素的存在情况,0代表不存在,1代表存在,因为二进制位宽最大为w,所以表示一个集合要被分成$n⁄w$块,每一块是一个w位的二进制整数...第i块表示第i*w~w*(i+1)-1个数...

交集:&,并集:|,补集:!,差集:x-y的差集,${a|a∈x&&a∉y}$,x^(x&y) ...

统计元素的个数

运用之前的求一个数1的个数对每一块分别统计相加...

遍历集合元素

遍历每一块的所有1...不断lowbit并且删去lowbit...

求集合中第k小的元素

首先找出第k小元素所在的块,然后二分查找具体位置...

求集合中大于x的最小元素(upper_bound)

从x所在的块开始向后遍历,找到第一个存在元素的块,用求后缀0的方式确定具体位置...

未完待续...

Bits的更多相关文章

  1. [LeetCode] Number of 1 Bits 位1的个数

    Write a function that takes an unsigned integer and returns the number of ’1' bits it has (also know ...

  2. [LeetCode] Reverse Bits 翻转位

    Reverse bits of a given 32 bits unsigned integer. For example, given input 43261596 (represented in ...

  3. 【leetcode】Number of 1 Bits

    题目描述: Write a function that takes an unsigned integer and returns the number of '1' bits it has (als ...

  4. Leetcode-190 Reverse Bits

    #190. Reverse Bits Reverse bits of a given 32 bits unsigned integer. For example, given input 432615 ...

  5. CodeForces 485C Bits[贪心 二进制]

    C. Bits time limit per test1 second memory limit per test256 megabytes inputstandard input outputsta ...

  6. uva12545 Bits Equalizer

    uva12545 Bits Equalizer You are given two non-empty strings S and T of equal lengths. S contains the ...

  7. LeetCode Counting Bits

    原题链接在这里:https://leetcode.com/problems/counting-bits/ 题目: Given a non negative integer number num. Fo ...

  8. Number of 1 Bits(Difficulty: Easy)

    题目: Write a function that takes an unsigned integer and returns the number of ’1' bits it has (also ...

  9. 解剖SQLSERVER 第五篇 OrcaMDF里读取Bits类型数据(译)

    解剖SQLSERVER 第五篇  OrcaMDF里读取Bits类型数据(译) http://improve.dk/reading-bits-in-orcamdf/ Bits类型的存储跟SQLSERVE ...

  10. 高级c++头文件bits/stdc++.h

    用这种方法声明头文件只需两行代码 #include<bits/stdc++.h> using namespace std; 这个头文件包含以下等等C++中包含的所有头文件: #includ ...

随机推荐

  1. 七、Linux 文件与目录管理

    Linux 文件与目录管理 我们知道Linux的目录结构为树状结构,最顶级的目录为根目录 /. 其他目录通过挂载可以将它们添加到树中,通过解除挂载可以移除它们. 在开始本教程前我们需要先知道什么是绝对 ...

  2. 十、MySQL 删除数据表

    MySQL 删除数据表 MySQL中删除数据表是非常容易操作的, 但是你再进行删除表操作时要非常小心,因为执行删除命令后所有数据都会消失. 语法 以下为删除MySQL数据表的通用语法: DROP TA ...

  3. yum仓库及配置

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 最近由于服务器需求,需要在公司内网搭建内网yum源. 搭建内网yum源需要分以下几个步骤,如下: 1. yum是什么 2. repo文件是什么 3. r ...

  4. python3:判断手机的亮屏状态

    在用python对手机做一些自动化操作时,常常会判断手机的亮屏状态,知晓手机的亮屏状态后才好做进一步的动作,如给屏幕解锁等.  用于了解手机的亮屏情况,有一个adb命令可用: adb shell du ...

  5. I miss you, Jenny【我想念你,jenny】

    I miss you, Jenny Forrest Gump: 阿甘正传 You died on a Saturday morning. And I had you placed here our t ...

  6. Codeforces Round #435 (Div. 2) B (二分图) C(构造)

    B. Mahmoud and Ehab and the bipartiteness time limit per test 2 seconds memory limit per test 256 me ...

  7. 9、python中的控制流

    学习完python的基础与数据后,我们就可以编写一些简单的命令了.但这时我们发现,目前位置写出来的程序都是自上而下顺序地执行的.要想程序改变这种自上而下的流程多一点变化,我们就要学习三种程序中的语句. ...

  8. HOJ_14001 Just Terraffic!

    题意相对来说比较扭曲..所以来说下模型,具体做法有兴趣的孩纸去问度娘或者波塞冬吧~~ 给出一个序列长度,并且输入该序列,该序列的含义是横坐标: 任何两个相邻坐标绝对值小于等于1000的必然为一个整体, ...

  9. LeetCode刷题感想

    断断续续用了半年的时间把LeetCode刷完了,之前复习了数据结构与算法.将刷题与复习数据结构结合起来会更有效果.总之不是为了刷题而刷题,而是为了巩固和补充一部分知识. LeetCode真的是一个很好 ...

  10. P2615 神奇的幻方

    P2615 神奇的幻方 题目描述 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,……,N*N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: 首 ...