位运算 - 异或求和

异或求和

Problem‘s Link: http://acm.xmu.edu.cn/JudgeOnline/problem.php?id=1254



Mean:

Description
给你2个区间[A,B]和[C,D],现在只要求你求出区间[A,B]和[C,D]内任意2个整数异或后的和,因为答案可能会很大,你只需将结果%mod即可。

For(i:A→B)
      For(j:C→D)
           Sum += (i^j);

Input
输入第一行为T(T<=15),表示测试的数据组数,第2行到T+1行,每行5个数字,分别为A,B,C,D,mod.(1<=A,B,C,D<2^31,A<=B,C<=D,1<=mod& lt;=1,000,000,007),即为上述的数值。

Output
输出有T行,每行有一个数字,代表题述的答案(即Sum % mod)。

Sample Input
3
5 11 9 12 43
9 12 5 11 83
1 1 2 2 3

Sample Output
18
24
0

analyse:

看到这么大的输入,再看看时限是500ms,开始有点摸不着头脑,看来只能用log(n)的方法来做了。

典型的位运算题目。

首先我们求出[A,B]区间中所有数各个位上1的个数。比如[1~8]就是1,4,4,4;

怎么求呢?

我们可以打表找一下规律:

1: 0001

2: 0010

3: 0011

4: 0100

5: 0101

6: 0110

7: 0111

8: 1000

9: 1001

.....

我们会发现,[1,n]中第k位总共1的个数就是:n/(k<<1) *(k<<1 /2 )。或者说:符合 x mod 2^k >=2^(k-1) 的 x 第k位就是1。

额,这个要自己去找规律。

要求[A~B]区间内各位上1的个数,只需要B1[i]-A1[i]就行。

同理,我们可以求出[C~D]区间上各位1的个数。

剩下的就是怎么求和了。

求和比较简单:

区间【A,B】第k位为1的个数乘以区间【C,D】第k位为0的个数+区间【A,B】第k位为0的个数乘以区间【C,D】第k位为1的个数)*2^(k-1)。

Time complexity: O(logn)

Source code: 

/*
* this code is made by crazyacking
* Verdict: Accepted
* Submission Date: 2015-08-05-11.38
* Time: 0MS
* Memory: 137KB
*/
#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#define  LL long long
#define  ULL unsigned long long
using namespace std;

void get1(LL n,LL arr[]) { /**< 求1~n所有数各个位上1的个数之和 */
     for(int i=1; i<32; ++i) arr[i]=0;
     for(LL i=1,f=2,b=1; b<=n; ++i,f<<=1,b<<=1) {
           arr[i]=(n/f)*(f/2); /**< 对于每一位,1~n可分为n/t组(t是每组的01数量),其中每组有t/2个是1 */
           if(n%f>=b) arr[i]+=(n%f-b+1); /**< 加上余数部分 */
     }
}

int main() {
     ios_base::sync_with_stdio(false);
     cin.tie(0);
     LL n,t;
     cin>>t;
     while(t--) {
           LL A,B,C,D,Mod,a1[32],b1[32],c1[32],d1[32];
           cin>>A>>B>>C>>D>>Mod;
           get1(A-1,a1); get1(B,b1); get1(C-1,c1); get1(D,d1);
           LL ab1[32],ab0[32],cd1[32],cd0[32];
           for(int i=1; i<32; ++i) ab1[i]=b1[i]-a1[i],cd1[i]=d1[i]-c1[i];
           int l1=B-A+1,l2=D-C+1;
           for(int i=1; i<32; ++i) ab0[i]=l1-ab1[i],cd0[i]=l2-cd1[i];
           LL sum=0,base=1;
           for(int i=1; i<32; ++i)
                 sum=(sum+(base<<(i-1))%Mod*(ab0[i]%Mod *cd1[i]%Mod +ab1[i]%Mod*cd0[i]%Mod)%Mod)%Mod;
           cout<<sum<<endl;
     }
     return 0;
}
/*

*/

时间: 2024-10-28 10:31:05

位运算 - 异或求和的相关文章

杭电 2095 find your present (2)【位运算 异或】

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2095 解题思路:因为只有我们要求的那个数出现的次数为奇数,所以可以用位运算来做,两次异或同一个数最后结果不变,那么就可以知道异或运算相当于过滤掉了出现次数为偶数的数,最后只留下了唯一的那一个出现次数为奇数的数. 反思:位运算好陌生,好好学. #include<stdio.h> int main() { int n; long int a; while(scanf("%d",&a

位运算 - 异或

用与或非来表示异或 通俗的说异或,即指两值相异时,取真值,否则为假. 以下是其真值表 | x^y |(y) 0 | 1 | |(x)-- | --: | :---: | | 0 | 0 | 1 | | 1 | 1 | 0 | 那么如何用与或非实现呢? 既然是相异,当然首先想到 x & ~y,看看它的真值表 | x & ~y | 0 | 1 | | ----- | --: | :---: | | 0 | 0 | 0 | | 1 | 1 | 0 | 有一个位子有点不一样,如果换一下 x 和 y

JavaSE7基础 使用位运算异或 进行两个变量的数值交换

jdk版本  :jdk-7u72-windows-i586系统     :Windows7编辑器   :Notepad++ v7.4.2注意事项 :博文内容仅供参考,不可用于其他用途. 代码 class Demo{ public static void main(String[] args){ int a=10; int b=20; //一般来讲,把这两个数交换可以申请一个中间变量temp //在高深一点的话,可以建立方程组 //然而,在视频教程中老师 却是这样做的... a = a ^ b;

位运算-异或运算-只出现一次的数字-给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找出那个只出现了一次的元素. 示例: 输入: [2,2,1] 输出: 1 输入: [4,1,2,1,2] 输出: 4 GO: func singleNumber(nums []int) int { var num int for i := 0; i < len(nums); i++ { num = num ^ nums[i]; } return num; } PHP: class Solution { /** * @par

C语言学习笔记:16_位运算

/* * 16_位运算.c * * Created on: 2015年7月6日 * Author: zhong */ #include <stdio.h> #include <stdlib.h> /* 位运算: * 所谓的位运算是指以二进制位为对象的运算. * c语言中位运算符有: * & 按位与 * | 按位或 * ^ 按位异或 * ~ 按位取反 * << 左移 * >> 右移 * * 除了 ~ 按位取反运算符以外,均为二目运算符. 两边的运算对象

基础位运算基本原理和应用

微信公众号 位运算是编程语言的基础,在看源码的时候会看到很多位运算代码,但是在项目代码中很少会看到位运算.因为应用代码中,有很多判断和计算都可以直接用数值的判断和计算完成,没有必要去用位运算,以至于这些基础的东西慢慢用的越来越少,慢慢也就忘了.导致的一个结果就是看源代码很费力,因为大量的位运算逻辑,看不懂.作为程序员感觉数据位运算是非常必要,有点如下: 看源码时能够更好的理解 位运算更接近计算机的习惯,执行的效率会更高 装逼利器,在项目中使用位运算,体现逼格 N种基本的位运算 位运算 -- 与运

位运算之 C 与或非异或

位运算比较易混: 位运算之 C 与或非异或 与运算:& 两者都为1为1,否则为0 1&1=1,  1&0=0,  0&1=0,  0&0=0 或运算:| 两者都为0为0,否则为1 1|1 = 1,  1|0 = 1,  0|1 = 1, 0|0 = 0 非运算:~ 1取0,0取1 ~1 = 0, ~0 = 1 ~(10001) = 01110 异或运算:^ 两者相等为0,不等为1(易混淆) 1^1=0, 1^0=1, 0^1=1, 0^0=0 位移操作符:<&

位运算常用操作总结位运算应用口诀清零取反要用与,某位置一可用或若要取反和交换,轻轻松松用异或移位运

来源:http://www.educity.cn/wenda/381487.html 位运算常用操作总结位运算应用口诀 清零取反要用与,某位置一可用或 若要取反和交换,轻轻松松用异或 移位运算 要点 1 它们都是双目运算符,两个运算分量都是整形,结果也是整形.     2 " $amp;     3 "$amp;>amp;>quot;$右移:右边的位被挤掉.对于左边移出的空位,如果是正数则空位补0,若为负数,可能补0或补1,这取决于所用的计算机系统.     4 "

C的|、||、&amp;、&amp;&amp;、异或、~、!运算 位运算

研究这个的起因是我遇到一个题目,判断一个数是奇偶数,这个很简单,但是又个最佳代码 判断奇偶时用了 #include int main() { int n; scanf("%d",&n); printf("%s",n&1?"odd":"even"); } 这里用&运算,从而判断二进制数的最后一位是否为当n的最后一位=0.和1& 则为 0 .只有当n = 1 则 &为 1 再根据?:表达式的