题目链接:http://poj.org/problem?id=3495
题目大意:
求等差数列的异或和。
给定
x
,
多组测试数据,
x,y,z≤232,x≤y
题解:
显然大数据时暴力计算会超时,那么我们就从位运算的性质下手解决这一问题:
异或操作中每一位互不干扰,所以我们可以分别计算出答案每一位的值。
那么第i位的值就为
(⌊x2i⌋+⌊x+z2i⌋+⌊x+2z2i⌋+……+⌊x+nz2i⌋)%2
令
a=z,b=x,c=2i
,那么就可以将问题转化为求
∑n−1x=0⌊ax+bc⌋
。
将式子中的
⌊ac⌋
和
⌊bc⌋
提取出来,
可得
∑n−1x=0⌊ax+bc⌋=⌊ac⌋∗n∗(n−1)2+⌊bc⌋∗n+∑n−1x=0⌊(a%c)x+(b%c)c⌋
⌊ac⌋∗n∗(n−1)2+⌊bc⌋∗n
可求,问题转化为了求
∑n−1x=0⌊(a%c)x+(b%c)c⌋
它的值等价于求下面的图中的整点数。
所求区域为青色区域和棕绿色区域。
图中青色点为所求整点。
易知棕绿色区域与灰色区域无整点(
b%cc<1,⌊(a%c)x+(b%c)c⌋−(a%c)n+(b%c)c<1
)
所以所求区域可看做青色与灰色部分,
我们令O’为原点,y轴负方向为x’轴正方向,x轴负方向为y’轴正方向,定义新的坐标系。
那么直线
y=⌊(a%c)x+(b%c)c⌋
就变为了
y=⌊cx+((an+b)%c)a%c⌋
所以
∑n−1x=0⌊ax+bc⌋=∑⌊(a%c)n+(b%c)c⌋−1x=0⌊cx+((an+b)%c)a%c⌋
观察变化前后的式子,发现
a
变成了
递归可以到当
n==0
时截止,也可以到当
an+b<c
时截止,因为此时答案必定为
0
附上直线变化的证明:
直线的斜率显然变为原来的倒数,由
直线的截距为图中蓝色线的长度,不难求出它的长度为
n+b%ca%c−⌊(a%c)n+(b%c)c⌋a%cc=n+b%ca%c−c⌊(a%c)n+(b%c)c⌋a%c
我们知道
p⌊qp⌋=q−q%p
所以
c⌊(a%c)n+(b%c)c⌋=n+b%ca%c−(an+b)%ca%c
所以截距为
(an+b)%ca%c
代码:
#include<stdio.h>
typedef unsigned int u;
u cal(u a,u b,u c,u n)
{
u re=0;
re+=(a/c)*n*(n-1)/2+(b/c)*n;
b%=c;
a%=c;
if(a*n+b<c)
{
return re;
}
else
{
return re+cal(c,(a*n+b)%c,a,(a*n+b)/c);
}
}
int main()
{
u x,y,z;
while(~scanf("%u%u%u",&x,&y,&z))
{
u ans=0;
u n=(y-x)/z+1;
for(int i=0;i<32;i++)
{
ans|=(cal(z,x,1u<<i,n)&1)<<i;
}
printf("%u\n",ans);
}
return 0;
}