Live2D

位运算

一、原码反码补码

  1. 原码:有符号整数 最高位1 0表示正负

    反码(一补数):正数同原码 负数符号位不变 其余位取反

    补码(二补数):正数同原码(S) 负数符号位不变 其余位取反 +1 (-1-(~S))

  2. 补码:自然溢出取,解决0唯一性问题

  3. 0x3F 3F 3F 3F (1 061 109 567) :

    1. 整数的两倍不超过0x7F FF FF FF(int 能表示最大整数)
    2. 整数的每八位都是相同的
    //memset(a,val,sizeof(a));
    // val: 0x00~0xFF  把val填充到a的每个字节上,一个int占用4个字节  
    // memset 只能赋值每八位都相同的int
    
    //把一个数组的数值初始化正无穷 
    memset(a,0x3f,sizeof(a)); 
    

二、 移位运算

  1. 左移:低位用0填充 高位越界舍弃 \(1<<n = 2^n\)

    右移:实现方式由编译器决定,通常使用算术右移

    ​ 算术右移:高位符号位填充 低位越界舍弃 (除以2向下取整)

    ​ 逻辑右移:高位0填充 低位越界舍弃

  2. \(a^b\) mod p ,\(1<=a,b,p<=10^9\)

    分析:

    快速幂: \(O(log_2{b})\)

    $b = c_{k-1}2^{k-1} + c_{k-2}2^{k-2} +···+c_{0}2^{0} $

    \(a^b =a^{ c_{k-1}*2^{k-1} }* a^{c_{k-2}*2^{k-2}} *···*a^{c_{0}*2^{0}}\)

    b => \(c_k\) , a=> \(a^{2^k}\)

int power(int a,int b,int p){
	int ans = 1%p;
	for(;b;b>>=1){
		if(b&1) s = (long long)ans*a%p; 
		a = (long long)a*a%p;
	}
	return ans;
}

 // 两个数值执行运算,以最高数值类型为标准
 // 两个int相乘,CPU只提供一个32位寄存器保存结果 容易造成越界 
 // 将ans转换为long long ,赋值隐式转换为int存入ans
  1. 64位整数乘法

    求a乘以b对p取模的值,其中 \(1<=a,b,p<=10^{18}\)

    分析:

    方法1:类似快速幂思想 \(2*10^{18} <10^{19}\) 没越界 \(O(log_2{b})\)

    $b = c_{k-1}2^{k-1} + c_{k-2}2^{k-2} +···+c_{0}2^{0} $

    \(a*b = a*c_{k-1}*2^{k-1} + a*c_{k-2}*2^{k-2} +···+a*c_{0}*2^{0}\)

long long mul(long long a,long long b,long long p){
  long long ans = 0;
  for(;b;b>>=1){
    if(b&1) ans = (ans+a)%p;
    a = a*2%p;
  }
  return ans;
}

​ 方法2:\(a*b \% p = a*b - \lfloor a*b/p \rfloor *p\)

\(a*b\)\(\lfloor a*b/p \rfloor *p\) 可能很大,但两者的差一定在 0~p-1之间 所以只用关注低位 运算溢出不影响

long long mul(long long a,long long b, long long p){
  a%=p; b%=p;
  long long c = (long double)a*b/p;
  long long ans = a*b - c*p;
  if(ans < 0) ans+=p;
  else if(ans >= p) ans-=p;
  return ans;
}

三、 二进制状态压缩

  1. 二进制状态压缩: 将一个长度为m的bool数组 用 一个m位二进制整数表示并存储 的方法

    ​ m 不太大 直接用一个整数类型存储

    ​ m较大 用若干个整数类型(int 数组)

    ​ 使用C++ STL 提供的 bitset 实现

操作 运算
取出整数n在二进制表示下的第k位 (n>>k)&1
取出整数n在二进制表示下的第0~k-1位(后k位) n&((1<<k)-1)
把整数n在二进制表示下的第k位取反 x xor (1<<k)
对整数n在二进制表示下的第k位赋值1 n|(1<<k)
对整数n在二进制表示下的第k位赋值0 n&(~(1<<k))
  1. 最短Hamilton路径(哈密顿)

给定一张n(n<=20)个点的带权无向图,点从0~n-1标号,求起点0到终点n-1的最短Hamilton路径。

Hamilton路径: 从0到n-1不重不漏的经过每个点恰好1次

分析:

​ 方法1:朴素算法 枚举全排列 求路径长度最小值 \(O(n*n!)\)

​ 方法2:二进制转态压缩DP \(O(n^2 * 2^n)\)

​ F[i,j] i表示状态(一个n位二进制数表示每个点被经过的状态 第i位二进制表示点i状态) j表示处于点j

​ F[i,j]表示 点被经过状态且处于点j时 的最短路径

\(\begin{equation} \left\{ ​ \begin{array}{lr} ​ F[1,0]=0 & \\ ​ F[i,j] = min{F[i \quad xor \quad(1<<j), \quad k]+weight(k,j)} \qquad 0 <= k<n , (i>>k)=1& ​ \end{array} \right.\end{equation}\)



int f[1<<20][20];
int hamilton(int n, int weight[20][20]){
  memset(f,0x3f,sizeof(f));
  f[1][0] = 0;
  for(int i=1;i < 1<<n; i++)
    for(int j=0;j<n;j++) 
      if(!(i>>j & 1))
        for(int k=0;k<n;k++)
          if(i>>k &1)
            f[i][j] = min(f[i][j],f[i ^ 1<<j][k]+weight[k][j]);
 return f[(1<<n)-1][n-1];
}

四、成对变换

  1. 对于非负整数n

    ​ $$x \quad xor \quad 1=
    \begin{cases}
    n+1& \text{n为偶数}\
    n-1& \text{n为奇数}
    \end{cases}$$

​ 所以 "0 与 1", "2 与 3", "3 与 4" 关于xor 1构成成对变换

  1. 应用
    1. 图论邻接表中边集的存储: 在无向边或双向边的图中,把正反方向的边放在数组n 和 n+1位置(n为偶数) 可以通过xor 1 获取当前边的反向边的存储位置

五、lowbit运算

  1. lowbit(n):取出非负整数n在二进制表示下最低位1以及它后面的0构成的数值

    ​ lowbit(n) = n & (~ n +1) = n & (-n)

  2. 应用

    1. Hash :可以找出整数二进制表示下所有是1的位,所花费的时间与1的个数同级
    2. 树状数组
posted @ 2019-04-23 01:21  Duiliur  阅读(357)  评论(1编辑  收藏  举报