您的位置:首页 > 其它

SWJTU oj Short Problem(异或求和)

2017-03-15 18:54 274 查看
Short Problem

发布时间: 2017年3月15日 12:14 时间限制: 1000ms 内存限制: 128M

描述

作为一个acmer最不能忍受的就是for循环两圈,因为很容易得到TLE,所以希望你们能优化下面的程序,由于结果可能很大,只要输出sum%mod的值即可(sum初始值为0,”^”表示异或操作)。

2271.jpg

输入

输入第一行为T(T≤2000),表示测试的数据组数,第二行到T+1行,每行5个数字,分别为a,b,c,d,mod (1≤a,b,c,d<2^31,a≤b,c≤d,1≤mod≤1,000,000,007).

输出

每行输出sum%mod的值即可.

样例输入1 复制

2

9 12 5 11 83

1 1 2 2 3

样例输出1

24

0

题解:

首先,需要将每个数化成二进制后考虑每个位上的1和0的个数来算。

如何快速求出一个区间内某位上1或0的个数呢:

符合 x mod 2^k >=2^(k-1) 的 x 第k位就是1,通过同余就可以求出1~a第k位是1的位数,然后减一减就是区间了。

最后对于每一位0和1的对数再乘上2^k-1就是对答案的贡献。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<set>
#include<ctime>
#include<queue>
#include<cmath>
#include<algorithm>
#define PI acos(-1.0)
using namespace std;
typedef long long LL;

int T;
LL pp[50];
LL a,b,c,d,mod;
LL X1[50],X0[50],Y1[50],Y0[50];
void init(){
pp[0]=1;
for(int i=1;i<=31;i++){
pp[i]=pp[i-1]*2;
}
}
LL cal(LL a,int i){
return (a/pp[i])*(pp[i-1])+(a%pp[i]>=pp[i-1]?a%pp[i]-pp[i-1]+1:0);
}

int main()
{
init();
scanf("%d",&T);
while(T--){
scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&mod);
for(int i=1;i<=31;i++){
LL t1=cal(a-1,i);
LL t2=a-t1;
LL t3=cal(b,i);
LL t4=b+1-t3;
X1[i]=t3-t1;
X0[i]=t4-t2;
}
for(int i=1;i<=31;i++){
LL t1=cal(c-1,i);
LL t2=c-t1;
LL t3=cal(d,i);
LL t4=d+1-t3;
Y1[i]=t3-t1;
Y0[i]=t4-t2;
}
LL ans=0;
for(int i=1;i<=31;i++){
ans=(ans+((X1[i]*Y0[i]%mod)*pp[i-1])%mod)%mod;
ans=(ans+((Y1[i]*X0[i]%mod)*pp[i-1])%mod)%mod;
}
printf("%lld\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: