掷硬币的位操作

时间:2013-06-02 10:19:48

标签: algorithm bit-manipulation

我试图理解这段代码。我认为它正在使用段树数据结构的一些变体,但我无法理解此代码中的任何位操作。

实际问题是有N币,我们有1个操作和1个查询

  1. 在范围[A,B]
  2. 范围内翻转硬币
  3. 分别查找[A,B]范围内的头数。
  4.  #define LEAVES (131072)
     #define MSB_V (0x80000000)
     #define MSB_P (31)
    
     int it[2 * LEAVES];
     int A, B;    //Query range
    
     void flip (int i, int min, int max)
     {
        if ((min > B) || (max <= A))
        {   }
        else if ((min >= A) && ((max-1) <= B))
        {   it[i] ^= MSB_V; }
        else
        {
            int l = 2 * i;
            int r = l + 1;
            int mid = (min + max) / 2;
            it[l] ^= it[i] & MSB_V;
            it[r] ^= it[i] & MSB_V;
            it[i] ^= MSB_V;
            flip(l, min, mid);
            flip(r, mid, max);
            it[i] = (it[l] >> MSB_P ? mid - min - (it[l] ^ MSB_V) : it[l]) +
                    (it[r] >> MSB_P ? max - mid - (it[r] ^ MSB_V) : it[r]);
        }
    }
    
    int count (int i, int min, int max)
    {
        int h;
        if ((min > B) || (max <= A))
        {   h = 0; }
        else if ((min >= A) && ((max-1) <= B))
        {   h = it[i] >> MSB_P ? max - min - (it[i] ^ MSB_V) : it[i]; }
        else
        {
            int l = 2 * i;
            int r = l + 1;
            int mid = (min + max) / 2;
            it[l] ^= it[i] & MSB_V;
            it[r] ^= it[i] & MSB_V;
            it[i] ^= MSB_V;
            it[i] = (it[l] >> MSB_P ? mid - min - (it[l] ^ MSB_V) : it[l]) +
                    (it[r] >> MSB_P ? max - mid - (it[r] ^ MSB_V) : it[r]);
            h = count(l, min, mid) + count(r, mid, max);
        }
        return h;
    }
    

    有人可以给我一些提示,说明所有这些位操作背后的逻辑是什么

1 个答案:

答案 0 :(得分:4)

it表示完整的二叉树;节点1是根节点,节点k的子节点是2k和2k + 1。

完整二叉树的叶子是硬币。内部节点是面向某种方式(低位31位)和“翻转”标记(位于符号位)的硬币数。如果翻转市场清晰,则低31位计算节点子树中的抬头硬币数量,而如果设置了翻转位,则计算尾部硬币。

此处findcount使用的参数约定是i是要计数或翻转的节点,min是该节点表示的最低索引,并且max是最高指数。 findcount中的前两个测试检查翻转/计数范围(由AB定义)是否与节点i不相交或包含的范围。

您在flip中看到的是:

  • 如果[A,B]与[min,max]不相交,则不执行任何操作。
  • 如果[A,B]包含[min,max],请翻转翻转的标记。
  • 否则,请对这两个孩子运行flip。一旦我们完成了这个,这里的不变量就毁了;通过将两个孩子中的单挑硬币数相加来修复它。

您在count中看到的是:

  • 如果[A,B]与[min,max]不相交,则返回0.
  • 如果[A,B]包含[min,max],则返回此节点中的计数。
  • 否则,加上我们孩子的数量。