原码,反码,补码
- (使用-1做例子)
- 原码:使用第一位表示符号,其他二进制位表示数。例如:1000_0001
- 反码:对于原码(除了第一个符号位),剩下的按位取反 。例如:1111_1110
- 补码:在反码基础上+1(对于负数来说,可以在原码找到第一位为1,之后按位取反(也是除去符号位))。
例如:1111_1111
位运算
以下运算均建立在二进制基础上
- 按位与:当相应位上的数都是1时,该位取1,否则该位为0。
- 按位或:只要相应位上存在1,那么该位就取1,否则取0。
- 按位异或:当相应位上的数字不相同时,该位取1,否则取0。
- 取反:每个位上都取相反值,1变成0,0变成1。
- 左移(<<):将一个数各二进制位全部向左移动若干位,用0来补最低位。(高位中不包含一的情况下左移n位即原数乘2n(溢出后舍弃))
- 右移(>>):将一个数各二进制位全部向右移动若干位。(高位中不包含一的情况下右移n位即原数除2n,再向下取整右移对于无符号类型强制补0,对于有符号类型续补符号位)
- 这部分可能没有针对特殊情况讨论,见谅。
例题
- 求a的b次方对于p取模的值,其中 1 <= a,b,p <=109。
分析:
- O(n)算法:
次方运算为相乘n次,由于加减乘除后直接取余和算出总结果后取余相同。
所以每乘一次取模一次即可。- O(lgn)算法:
分析:
因为任意一个正整数数都可以表示为若干不重复的2的次幂的和。
所以 b = ck-1*2k-1 + ck-2*2k-2 + … + c0*20
所以 a = a c k − 1 ∗ 2 k − 1 a^{c_{k - 1}*2^{k-1}} ack−1∗2k−1 * a c k − 2 ∗ 2 k − 2 a^{c_{k - 2}*2^{k-2}} ack−2∗2k−2 * … * a c 0 ∗ 2 0 a^{c_{0}*2^{0}} ac0∗20
所以对于每一个b的为1的位乘起来取模
/*
求a的b次方对p取模的值,1<=a,b,p<=10**9.
*/
import java.util.Scanner;
import java.io.BufferedInputStream;
import java.io.PrintWriter;
import java.io.BufferedOutputStream;
public class ExOne {
public static void main(String[] args) {
Scanner in = new Scanner(new BufferedInputStream(System.in));
PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out), true);
long a = in.nextInt();
long b = in.nextInt();
long p = in.nextInt();
//时间复杂度为O(n)的算法:
long res = a;
//out.println("start res = " + res);
for (long i = 0; i < b - 1; i++) {
res *= a;
//out.println("after multiply res = " + res);
res %= p;
//out.println("after divmod res = " + res);
out.println(res);
}
out.println(res);
//时间复杂度为O(lgn)的算法:
long res = 1 % p;
//通过右移和&1可以做到递推整个b的二进制的每一位的值
//右移
for (; b != 0; b >>= 1) {
//看最低位值为1还是0
if ((b & 1) == 1) {
res = (long)res * a % p;
}
a = (long)a * a % p;
}
out.println(res);
}
}