acm竞赛位运算简介及实用技巧
位运算简介及实用技巧
转载者注:最近在论坛上看到些十分空虚、空大的帖子。觉得编程还是要讲点实践,实践出真知。
众所周知,人和电脑处理的方式究竟还是不同的,否则人人都是计算机程序员了。有些东西对人说很容易,而对计算机来说很难,反之亦然。
位操作就是人和电脑处理方式不同的体现,有些人认为这个东西有些BT,但其实非常高效的程序大多都是用位操作优化,因为它十分底层,速度极快。其实位操作也有他自己独特的性质,只要我们能熟练得掌握,就可以更好得驾驭我们的程序,这也是我转此帖的目的。
PS:不要把注意力集中在语言上,所有语言都是一样的,只是工具而已。
去年年底写的关于位运算的日志是这个Blog里少数大受欢迎的文章之一,很多人都希望我能不断完善那篇文章。后来我看到了不少其它的资料,学习到了更多关于位运算的知识,有了重新整理位运算技巧的想法。从今天起我就开始写这一系列位运算讲解文章,与其说是原来那篇文章的follow-up,不如说是一个remake。当然首先我还是从最基础的东西说起。
什么是位运算?
程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and
11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理):
110
AND 1011
----------
0010 --> 2
由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。当然有人会说,这个快了有什么用,计算6 and 11没有什么实际意义啊。这一系列的文章就将告诉你,位运算到底可以干什么,有些什么经典应用,以及如何用位运算优化你的程序。
Pascal和C中的位运算符号
下面的a和b都是整数类型,则:
C语言 | Pascal语言
-------+-------------
a & b | a and b
a | b | a or b
a ^ b | a xor b
~a | not a
a << b | a shl b
a >> b | a shr b 注意C中的逻辑运算和位运算符号是不同的。520|1314=1834,但520||1314=1,因为逻辑运算时520和1314都相当于True。同样的,!a和~a也是有区别的。
各种位运算的使用
=== 1. and运算 ===
and运算通常用于二进制取位操作,例如一个数 and 1的结果就是取二进制的最末位。这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数.
=== 2. or运算 ===
or运算通常用于二进制特定位上的无条件赋值,例如一个数or 1的结果就是把二进制最末位强行变成1。如果需要把二进制最末位变成0,对这个数or 1之后再减一就可以了,其实际意义就是把这个数强行变成最接近的偶数。
=== 3. xor运算 ===
xor运算通常用于对二进制的特定一位进行取反操作,因为异或可以这样定义:0和1异或0都不变,异或1则取反。
xor运算的逆运算是它本身,也就是说两次异或同一个数最后结果不变,即(a xor b) xor b = a。xor运算可以用于简单的加密,比如我想对我MM说1314520,但怕别人知道,于是双方约定拿我的生日19880516作为密钥。1314520 xor 19880516 = 20665500,我就把20665500告诉MM。MM再次计算20665500 xor 19880516的值,得到1314520,于是她就明白了我的企图。
下面我们看另外一个东西。定义两个符号#和@(我怎么找不到那个圈里有个叉的字符),这两个符号互为逆运算,也就是说(x # y)
@ y = x。现在依次执行下面三条命令,结果是什么?
复制内容到剪贴板
代码:
x <- x # y
y <- x @ y
x <- x @ y
执行了第一句后x变成了x # y。那么第二句实质就是y <- x # y @ y,由于#和@互为逆运算,那么此时的y变成了原来的x。第三句中x实际上被赋值为(x # y) @ x,如果#运算具有交换律,那么赋值后x就变成最初的y了。这三句话的结果是,x和y的位置互换了。(转载者注:怪不得有些东西可以这么用,这才是实质啊!)
加法和减法互为逆运算,并且加法满足交换律。把#换成+,把@换成-,我们可以写出一个不需要临时变量的swap过程(Pascal)。
复制内容到剪贴板
代码:
procedure swap(var a,b:longint);
begin a:=a + b;
b:=a - b;
a:=a - b;
end;
好了,刚才不是说xor的逆运算是它本身吗?于是我们就有了一个看起来非常诡异的swap过程:
复制内容到剪贴板
代码:
procedure swap(var a,b:longint);
begin
a:=a xor b;
b:=a xor b;
a:=a xor b;
end;
=== 4. not运算 ===
not运算的定义是把内存中的0和1全部取反。使用not运算时要格外小心,你需要注意整数类型有没有符号。如果not的对象是无符号整数(不能表示负数),那么得到的值就是它与该类型上界的差,因为无符号类型的数是用$0000到$FFFF依次表示的。下面的两个程序(仅语言不同)均返回65435。
复制内容到剪贴板
代码:
var
a:word;
begin
a:=100;
a:=not a;
writeln(a);
end.
复制内容到剪贴板
代码:
#include
int main()
{
unsigned short a=100; a = ~a;
printf( "%d\n", a );
return 0;
}
如果not的对象是有符号的整数,情况就不一样了,稍后我们会在“整数类型的储存”小节中提到。
=== 5. shl运算 ===
a shl b就表示把a转为二进制后左移b位(在后面添b个0)。例如100的二进制为1100100,而110010000转成十进制是400,那么100 shl 2 = 400。可以看出,a shl b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2。
通常认为a shl 1比a * 2更快,因为前者是更底层一些的操作。因此程序中乘以2的操作请尽量用左移一位来代替。
定义一些常量可能会用到shl运算。你可以方便地用1 shl 16 - 1来表示65535。很多算法和数据结构要求数据规模必须是2的幂,此时可以用shl来定义Max_N等常量。
=== 6. shr运算 ===
和shl相似,a shr b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。我们也经常用shr 1来代替div 2,比如二分查找、堆的插入操作等等。想办法用shr代替除法运算可以使程序效率大大提高。最大公约数的二进制算法用除以2操作来代替慢得出奇的mod运算,效率可以提高60%。
位运算的简单应用
有时我们的程序需要一个规模不大的Hash表来记录状态。比如,做数独时我们需要27个Hash表来统计每一行、每一列和每一个小九宫格里已经有哪些数了。此时,我们可以用27个小于2^9的整数进行记录。例如,一个只填了2和5的小九宫格就用数字18表示(二进制为000010010),而某一行的状态为511则表示这一行已经填满。需要改变状态时我们不需要把这个数转成二进制修改后再转回去,而是直接进行位操作。在搜索时,把状态表示成整数可以更好地进行判重等操作。这道题是在搜索中使用位运算加速的经典例子。以后我们会看到更多的例子。
下面列举了一些常见的二进制位的变换操作。
功能 | 示例 | 位运算
----------------------+---------------------------+--------------------
去掉最后一位 | (101101->10110) | x shr 1
在最后加一个0 | (101101->1011010) | x shl 1
在最后加一个1 | (101101->1011011) | x shl 1+1
把最后一位变成1 | (101100->101101) | x or 1
把最后一位变成0 | (101101->101100) | x or 1-1
最后一位取反 | (101101->101100) | x xor 1
把右数第k位变成1 | (101001->101101,k=3) | x or (1 shl (k-1)) 把右数第k位变成0 | (101101->101001,k=3) | x and not (1 shl (k-1))
右数第k位取反 | (101001->101101,k=3) | x xor (1 shl (k-1))
取末三位 | (1101101->101) | x and 7
取末k位 | (1101101->1101,k=5) | x and (1 shl k-1)
取右数第k位 | (1101101->1,k=4) | x shr (k-1) and 1
把末k位变成1 | (101001->101111,k=4) | x or (1 shl k-1)
末k位取反 | (101001->100110,k=4) | x xor (1 shl k-1)
把右边连续的1变成0 | (100101111->100100000) | x and (x+1)
把右起第一个0变成1 | (100101111->100111111) | x or (x+1)
把右边连续的0变成1 | (11011000->11011111) | x or (x-1)
取右边连续的1 | (100101111->1111) | (x xor (x+1)) shr 1
C语言程序设计位运算
C语言程序设计位运算
位运算是一种常用的C语言程序设计技巧,可用于对二进制数据进行高效操作。通过直接操作二进制位,位运算可以实现很多功能,例如位与、位或、位异或、位取反等运算,它们在数字计算、逻辑运算、数据压缩和加密算法等领域中发挥着重要作用。本文将介绍C语言中常见的位运算操作及其应用。
1.位与运算(&):用于提取两个操作数对应位置上的公共位。当两个操作数对应位置上的位都为1时,结果位为1,否则结果位为0。位与运算主要用于屏蔽操作和提取二进制数据的一些位。例如:
```c
int x = 12; // 二进制表示为 1100
int y = 10; // 二进制表示为 1010
int result = x & y; // 结果为 1000,即8
```
2.位或运算(,):用于将两个操作数对应位置上的位按位相加。当两个操作数对应位置上的位至少有一个为1时,结果位为1,否则结果位为0。位或运算主要用于设置位和合并二进制数据。例如:
```c
int x = 12; // 二进制表示为 1100
int y = 10; // 二进制表示为 1010
int result = x , y; // 结果为 1110,即14 ```
3.位异或运算(^):用于将两个操作数对应位置上的位进行异或操作。当两个操作数对应位置上的位相同时,结果位为0,否则结果位为1、位异或运算主要用于数据加密和数据校验。例如:
```c
int x = 12; // 二进制表示为 1100
int y = 10; // 二进制表示为 1010
int result = x ^ y; // 结果为 0110,即6
```
4.位取反运算(~):用于对操作数的每一个二进制位进行取反操作。当操作数其中一位置上的位为1时,取反结果中对应位置为0,反之为1、位取反运算主要用于反转二进制数据。例如:
oi 位运算
oi 位运算
OI 位运算——算法竞赛中的一把利器
算法竞赛中,位运算一直都是一把很有威力的利器。而且它应用广泛,可以用来解决各种问题。在OI竞赛中,掌握位运算技巧会为选手的获胜增添一份保障。本文将分别介绍常用的位运算操作符,以及它们的具体应用。
一、右移运算符
右移运算符(>>)可以将一个数的二进制位向右移动n位,移动后补零。比如,10的二进制码为1010,右移1位,得到0101,即5。右移运算常用于对2取整。
二、左移运算符
左移运算符(<<)可以将一个数的二进制位向左移动n位,移动后补零。比如,4的二进制码为0100,左移1位,得到1000,即8。左移运算常用于从10进制转换为2进制,以及计算2的n次幂。
三、与运算符
与运算符(&)会对两个数的二进制位进行与运算,只有两个二进制位都是1时,结果才为1。比如,对于3和5进行与运算,得到结果1。与运算常用于判断一个数的二进制位的某个二进制位是否为1,或者提取二进制位的部分信息。
四、或运算符
或运算符(|)会对两个数的二进制位进行或运算,只要两个二进制位中有一个是1,结果就为1。比如,对于3和5进行或运算,得到结果7。或运算常用于将二进制位的某些位置为1,或者将多个二进制位合并成一个整数。
五、异或运算符
异或运算符(^)会对两个数的二进制位进行异或运算,只有两个二进制位不相同时,结果才为1。比如,对于3和5进行异或运算,得到结果6。异或运算常用于交换两个数的值,或者判断两个数的二进制位的某些位置是否相同。
六、取反运算符
取反运算符(~)会对一个数的二进制位进行取反操作。比如,对于3进行取反操作,得到结果为-4。取反运算常用于取得整数的相反数,或者反转二进制位的值。
综述,位运算在算法竞赛中是占有举足轻重的地位的。掌握位运算技巧可以帮助选手更高效地解决问题,提升自己的竞赛水平。在实际应用中,位运算要多想,多练,才能真正掌握技巧,更好地应用位运算的威力。
位运算原理
位运算原理
位运算是一种对二进制数进行操作的数学运算方法。在计算机中,数据以二进制表示,位运算可以直接对二进制数的每一位进行操作,从而实现针对特定位的快速运算。
位运算包括以下几种常见操作:
1. 与运算(&):对两个二进制数的相应位进行逻辑与操作,只有两个对应位都为1时结果才为1,否则为0。
2. 或运算(|):对两个二进制数的相应位进行逻辑或操作,只要两个对应位中有一个为1时结果就为1,否则为0。
3. 非运算(~):对一个二进制数的每一位进行逻辑非操作,即0变为1,1变为0。
4. 异或运算(^):对两个二进制数的相应位进行逻辑异或操作,只有两个对应位不相同时结果为1,否则为0。
5. 左移运算(<<):将一个二进制数的各位都向左移动若干位,右边空出的位用0填充。
6. 右移运算(>>):将一个二进制数的各位都向右移动若干位,左边空出的位用原来的符号位填充。
位运算的快速性和效率使得它在计算机底层的编程中得到广泛应用。比如,可以用位运算来实现一些常见的操作,如判断一个数是否为奇数或偶数、交换两个变量的值、求两个数的平均数等。此外,位运算还可以用于掩码操作、嵌入式系统开发等领域。
需要注意的是,位运算往往涉及到二进制数的位数和位的顺序,因此在进行位运算时要特别小心,确保操作的正确性和合理性。
位操作基础篇之位操作全面总结
位操作基础篇之位操作全⾯总结
位操作篇共分为基础篇和提⾼篇,基础篇主要对位操作进⾏全⾯总结,帮助⼤家梳理知识。提⾼篇则针对各⼤IT公司如微
软、腾讯、百度、360等公司的笔试⾯试题作详细的解答,使⼤家能熟练应对在笔试⾯试中位操作题⽬。
下⾯就先来对位操作作个全⾯总结,欢迎⼤家补充。
在计算机中所有数据都是以⼆进制的形式储存的。位运算其实就是直接对在内存中的⼆进制数据进⾏操作,因此处理数据的
速度⾮常快。
在实际编程中,如果能巧妙运⽤位操作,完全可以达到四两拨千⽄的效果,正因为位操作的这些优点,所以位操作在各⼤IT
公司的笔试⾯试中⼀直是个热点问题。因此本⽂将对位操作进⾏如下⽅⾯总结:
⼀. 位操作基础,⽤⼀张表描述位操作符的应⽤规则并详细解释。
⼆. 常⽤位操作⼩技巧,有判断奇偶、交换两数、变换符号、求绝对值。
三. 位操作与空间压缩,针对筛素数进⾏空间压缩。
四. 位操作的趣味应⽤,列举了位操作在⾼低位交换、⼆进制逆序、⼆进制中1的个数以及缺失的数字这4种趣味应⽤。
希望读者能认真学习和亲⾃上机输⼊代码进⾏实验,相信通过本⽂及适当的练习可以使你对位操作有更加深⼊的了解,在笔
试⾯试中遇到位操作相关试题能更加从容。
⼀. 位操作基础
基本的位操作符有与、或、异或、取反、左移、右移这6种,它们的运算规则如下所⽰:
符号 描述 运算规则 by MoreWindows
& 与两个位都为1时,结果才为1
| 或 两个位都为0时,结果才为0
^ 异或两个位相同为0,相异为1
~ 取反0变1,1变0
<< 左移各⼆进位全部左移若⼲位,⾼位丢弃,低位补0
>> 右移各⼆进位全部右移若⼲位,对⽆符号数,⾼位补0,有符号数,各编译器处理⽅法不⼀样,有的补符号位(算术
右移),有的补0(逻辑右移)
注意以下⼏点:
1. 在这6种操作符,只有~取反是单⽬操作符,其它5种都是双⽬操作符。
2. 位操作只能⽤于整形数据,对float和double类型进⾏位操作会被编译器报错。
位运算技巧
- 1 - 位运算技巧
位运算是计算机科学中非常重要的技术,它被广泛应用于软件设计和实现中。此外,它还可用于实现更高效的算法,如排序和搜索算法。许多软件开发人员可能不熟悉位运算,但它可以有效地提高程序性能。
位运算技巧是指使用位运算来完成特定任务或操作的技术。它可以应用于处理数据,实现算法和执行指令,以替代传统技术,从而提高程序性能,缩短程序执行时间和减少代码行数。
位运算通常使用2进制和16进制表示法来表示数字,可以用位操作符(AND、OR、NOT)来更改并处理数据。常见的位操作技巧包括位运算移位,位检查,位测试,位组合计算,位清楚和位设置等。
位移位运算是指将数字右移或左移以反映更高一级的数字或位数。它可以帮助我们清楚地在2进制或16进制中表示数字,并可以更快地计算。例如,在2进制中,给定十进制数字11,可以通过右移2位(001100b)将其转换为十进制数字3。
位检查运算可以用来检查给定数据中的特定位是否为1,从而用于算法的选择和控制。例如,可以使用位检查运算执行奇偶检查,以及更复杂的算法,例如汉明距离,以计算两个字符串之间的匹配程度。
位测试与位检查类似,但是位测试会检查特定位上的值是否等于一个预定义的值,而不是仅检查其是否为1。例如,可以使用位测试运算检查特定字节内的值是否满足某些特定条件。
位组合计算可以用来将位从一组特定的数据中捕获,并将其合并 - 2 - 成一个新的数字。这在处理复杂的数据结构时非常有用,可以将结构中的位组合起来,以生成新的值。
清除位运算可用于清除/禁用/取消给定数据中的特定位。例如,可以使用清除位运算清除不需要的符号,或者让字节只具有所需的信息。
位设置运算是指将特定位设置为1,以触发特定的行为。例如,可以使用位设置运算来触发标志位,从而改变程序的执行流程。
位运算技巧在计算机科学中有着重要的地位,它可以用来实现更快的算法,更高效的程序,更少的代码行数,以及更快的代码执行时间。虽然它可能需要一定的学习成本,但一旦掌握了位运算技巧,它就可以显著提高代码性能和可维护性。
位运算 顺序
位运算 顺序
位运算是一种常用的计算机运算方法,它将数据表示为由0和1组成的二进制形式,并通过对这些二进制位进行逻辑运算来实现各种操作。本文将从不同的角度介绍位运算的相关内容,包括位运算的基本原理、常用的位运算操作符、位运算在计算机科学中的应用以及位运算的优缺点。
一、位运算的基本原理
位运算是基于二进制数字的逻辑运算,它包括与、或、非和异或等操作。与运算(&)将两个操作数的对应位进行逻辑与操作,只有当两个对应位都为1时,结果才为1;或运算(|)将两个操作数的对应位进行逻辑或操作,只有当两个对应位都为0时,结果才为0;非运算(~)将操作数的每个位取反;异或运算(^)将两个操作数的对应位进行异或操作,相同为0,不同为1。
二、常用的位运算操作符
在编程中,位运算操作符可以用来对数据进行二进制形式的操作。常用的位运算操作符有:
1. 按位与(&):将两个操作数的对应位进行与操作;
2. 按位或(|):将两个操作数的对应位进行或操作;
3. 按位非(~):将操作数的每个位取反;
4. 按位异或(^):将两个操作数的对应位进行异或操作;
5. 左移(<<):将操作数的二进制表示向左移动指定的位数; 6. 右移(>>):将操作数的二进制表示向右移动指定的位数。
三、位运算在计算机科学中的应用
位运算在计算机科学中有广泛的应用,下面分别介绍几种常见的应用:
1. 位掩码:通过位运算可以对某些特定的位进行掩码操作,从而提取出特定的信息。例如,在图形处理中,可以使用位掩码来提取RGB颜色的红、绿、蓝三个分量;
2. 位图:位图是一种用于表示图像的数据结构,其中每个像素被表示为一个位。位运算可以用于对位图的像素进行高效的操作,例如颜色反转、缩放、旋转等;
3. 压缩算法:位运算可以用于压缩算法中,通过对数据进行位操作,可以将数据压缩到更小的体积,从而节省存储空间和传输带宽;
4. 数据加密:位运算可以用于数据加密算法中,通过对数据进行位操作,可以实现数据的加密和解密;
acm算法类型
ACM(ACM International Collegiate Programming Contest)是一个国际性的大学生计算机程序设计竞赛,涵盖了多个算法类型。下面是一些常见的ACM算法类型:
贪心算法(Greedy Algorithm):贪心算法通过每一步选择当前最优解,以期望达到全局最优解的算法。在ACM竞赛中,贪心算法通常用于求解优化问题,如最小生成树、最短路径和调度问题等。
动态规划(Dynamic Programming):动态规划是一种将复杂问题分解为简单子问题并进行逐步求解的方法。ACM竞赛中,动态规划常用于解决最优化问题,如背包问题、最长公共子序列和最短编辑距离等。
图论算法(Graph Algorithms):图论算法涉及到处理和分析图结构的问题。在ACM竞赛中,图论算法常用于解决最短路径、最小生成树、网络流等问题,如Dijkstra算法、Floyd-Warshall算法和最大流最小割算法等。
字符串算法(String Algorithms):字符串算法主要处理字符串操作和匹配问题。在ACM竞赛中,字符串算法常用于解决字符串匹配、模式识别和字符串编辑等问题,如KMP算法、正则表达式和后缀树等。
数论算法(Number Theory Algorithms):数论算法涉及到整数和数字理论方面的计算问题。在ACM竞赛中,数论算法常用于解决素数判定、最大公约数、快速幂等问题,如欧几里得算法和质因数分解等。
排序和搜索算法(Sorting and Searching Algorithms):排序和搜索算法用于处理数据的排序和查找问题。在ACM竞赛中,常见的排序算法有快速排序、归并排序和堆排序,常见的搜索算法有二分搜索和广度优先搜索(BFS)等。
这些是ACM竞赛中常见的算法类型,但并不是全部。ACM竞赛还涉及其他算法和数据结构,如树结构、并查集、位运算等。为了在ACM竞赛中取得好的成绩,熟悉这些算法类型,并进行相关的训练和实践是非常重要的。
位运算技巧 2
- 1 - 位运算技巧
位运算是一类非常重要的数据处理技术,被广泛应用于计算机编程以及计算机系统的设计和实现中。它是按位处理信息的一种技术,它的基础是输入与输出的状态全部是存在二进制位中的,而实现位运算的关键是对位的表示和操作。
位运算可分为两类:涉及位的原子操作和涉及位的组合操作,比如AND运算、OR运算、XOR运算等。中原子操作是指只涉及单个位的操作,包括清零(CLEAR)、置位(SET)、复制(COPY)、取反(NEGATE)等操作,其用来处理的数据量是小的。而组合操作涉及多个位的操作,它们对数据量的处理无穷大,它们的优势在于可以在单次操作中完成对多个位的计算,并且,它们可以被完整地表示为计算机指令。
比如AND运算,它是在数学中常用的运算符号,它的含义是“与”,即如果两个输入位都是1时,则输出位为1,否则输出位为0。OR运算的意思是“或”,即如果两个输入位中有一个是1,则输出位为1,否则输出位为0。而XOR运算的意思是“异或”,即如果两个输入位是相同的,则输出位为0,否则输出位为1。
位运算技巧是由于位运算的特点,以及数据的位表示形式和位运算操作相结合而产生的一些技术手段,它们可以大大简化对位的操作,提高计算效率。比如,高效的乘法和除法算法就是利用位运算技巧来实现的;再如,两个数的比较和排序也可以利用位的操作来完成;甚至,用一些特殊的位运算技巧,我们还能够替代更为复杂的数值计算,如数据压缩、汉明重量计算等。 - 2 - 另外,位运算技巧还有另一个重要的功能,即可以帮助我们更好地理解机器语言,从而更好地开发算法和实现模型。比如,机器语言中控制字、触发字和指令也可以利用位运算技巧将它们进行组合,这种组合方式可以更加高效地实现算法和模型的实现;其次,位运算技巧还可以实现一些比较复杂的控制逻辑,这些技术不仅可以提高编程的效率,而且可以更加简洁地实现控制逻辑,这极大地降低了系统设计和开发的难度。
[ACM]前缀和差分位运算Hash函数
[ACM]前缀和差分位运算Hash函数
前缀和 & 差分 & 位运算 & Hash函数
1____前缀和
前缀和是⼀种重要的预处理,能⼤⼤降低查询的时间复杂度。可以简单理解为数列的前 n 项的和。
例1
给定⼀个长度为 n 的数列,请你求出数列中每个数的⼆进制表⽰中 1 的个数。
输⼊格式
第⼀⾏包含整数 n。
第⼆⾏包含 n 个整数,表⽰整个数列。
输出格式
共⼀⾏,包含 n 个整数,其中的第 i 个数表⽰数列中的第 i 个数的⼆进制表⽰中 1 的个数。
数据范围
1≤n≤100000,
0≤数列中元素的值≤ 109数列中元素的值≤109
输⼊样例:
5
1 2 3 4 5
输出样例:
1 1 2 1 2
#include
using namespace std;
int const maxn = 1e5 + 5;
int a[maxn] , dp[maxn], an[maxn];
void solve(){
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; ++i) cin >> a[i];
long long ans = 0;
dp[0] = 1;
for(int i = 1; i <= n; ++i){
int te = (a[i] + an[i - 1]) % k;
ans += dp[te];
dp[te]++;
an[i] = (an[i - 1] + a[i]) % k;
}
cout << ans << endl;
}
int main (){
solve();
}
例2
最⼤数
N个数围成⼀圈,要求从中选择若⼲个连续的数(注意每个数最多只能选⼀次)加起来,问能形成的最⼤的和。
输⼊描述:
第⼀⾏输⼊N,表⽰数字的个数,第⼆⾏输⼊这N个数字。
输出描述:
输出最⼤和。
样例输⼊:
8
2 -4 6 -1 -4 8 -1 3
样例输出:
14
数据范围及提⽰:
40% 1<=N<=300
60% 1<=N<=2000
C语言位运算与位操作技巧
C语言位运算与位操作技巧
位运算与位操作是C语言中常用的操作技巧,能够提高程序的执行效率,简化代码,以及实现一些特定的功能。本文将介绍C语言中常见的位运算操作及技巧。
一、按位与(&)操作
按位与操作符(&)用来将两个操作数的每一位进行逻辑与操作。例如,下面的代码演示了如何使用按位与操作将一个数的特定位清零:
```c
unsigned int clearBit(unsigned int num, int position) {
unsigned int mask = ~(1 << position);
return num & mask;
}
```
这段代码中,按位与操作num和mask可以清零num中指定位置上的位。
二、按位或(|)操作
按位或操作符(|)用来将两个操作数的每一位进行逻辑或操作。例如,下面的代码演示了如何使用按位或操作将一个数的特定位设置为1:
```c unsigned int setBit(unsigned int num, int position) {
unsigned int mask = 1 << position;
return num | mask;
}
```
这段代码中,按位或操作num和mask可以将num中指定位置上的位设置为1。
三、按位异或(^)操作
按位异或操作符(^)用来将两个操作数的每一位进行逻辑异或操作。例如,下面的代码演示了如何使用按位异或操作交换两个变量的值:
```c
void swap(int* a, int* b) {
*a = *a ^ *b;
*b = *a ^ *b;
*a = *a ^ *b;
}
```
这段代码中,连续三次按位异或操作实现了a和b的值互换。 四、按位取反(~)操作
按位取反操作符(~)用来对一个操作数的每一位进行逻辑取反操作。例如,下面的代码演示了如何使用按位取反操作求一个数的负数:
