码迷,mamicode.com
首页 > 其他好文 > 详细

noip 模拟赛 T3

时间:2018-10-15 20:34:24      阅读:143      评论:0      收藏:0      [点我收藏+]

标签:turn   快速   algo   范围   怎么   需求   异或运算   str   构造   

问:如何快速求出等差数列异或和?

玄学题...

对于异或运算,我们可以分开考虑每一位是1还是0,这样会好做一些

于是我们发现,每一位是一还是0的判别式如下:

设读入的数为x,y,z,等差数列共n项

第i位的值=∑[x+kz/2^i]mod 2 ,k∈[0,n-1]

然后怎么求?

令x=b,k=x,z=a,2^i=c于是

原式=∑[(ax+b)/c]mod 2 ,x∈[0,n-1]

这样:

原式=[a/c]n(n-1)/2+[b/c]n+∑[((a%c)x+b%c)/c] mod 2

前两项可以O(1)求,我们关注一下第三项

我们构造一条直线y=(a%c)/c x+(b%c)/c

那么所求的答案就是直线下方在(0,n-1]内整点的个数

于是我们重构一下坐标系:

令x=n,求出y=[(a%c)/c x+(b%c)/c],以(x,y)为原点,原x轴负方向为y轴正方向,原y轴负方向为x轴正方向建立新坐标系

那么我们会发现,所求的答案范围并没有变,仍然是刚才说的那些点的范围,但是我们可以换一个方式来求:

重构坐标系以后,每个点的横纵坐标交换,所以新的直线斜率是原来的倒数,即c/(a%c)

于是我们只需求出新的截距就能确定新的这条直线

以下是求截距的方程:

如果想求出截距,我们需要解出原坐标系中y=[(a%c)/c n+(b%c)/c]时在直线y=(a%c)/c x+(b%c)/c上对应的x值

所以我们要求解方程[(a%c)/c n(b%c)/c]=(a%c)/c x+(b%c)/c

最后解出x=n-(an+b)%c

这样截距就是n-x=(an+b)%c/(a%c)

这样新直线也就定下来了

那么我们所求也就转成了:

∑((cx+(an+b))/(a%c)),x∈[0,[((a%c)/c)n+(b%c)/c]-1]

发现这个表达式与原表达式结构相同,于是我们递归求解即可

注意上面的[x]为高斯函数,含义为向下取整

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
ll xx,y,z;
ll get_ans(ll a,ll x,ll b,ll c)
{
    ll ret=0;
    ret+=x*(b/c)%2+(x-1)*x/2*(a/c)%2;    
    a%=c;
    b%=c;
    if(a*x+b<c)
    {
        return ret%2;
    }else
    {
        return (ret%2+get_ans(c,(a*x+b)/c,(a*x+b)%c,a))%2;
    }
}
int main()
{
    freopen("C.in","r",stdin);
    freopen("C.out","w",stdout);
    scanf("%I64d%I64d%I64d",&xx,&y,&z);
    ll n=(y-xx)/z+1;
    ll ans=0;
    for(int i=0;i<32;i++)
    {
        ans|=((get_ans(z,n,xx,(1ll<<i))%2)<<i);
    }
    printf("%I64d\n",ans);
    return 0;
}

 

noip 模拟赛 T3

标签:turn   快速   algo   范围   怎么   需求   异或运算   str   构造   

原文地址:https://www.cnblogs.com/zhangleo/p/9792700.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!