noip 模拟赛 T3
Posted zhangleo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了noip 模拟赛 T3相关的知识,希望对你有一定的参考价值。
问:如何快速求出等差数列异或和?
玄学题...
对于异或运算,我们可以分开考虑每一位是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 ",ans); return 0; }
以上是关于noip 模拟赛 T3的主要内容,如果未能解决你的问题,请参考以下文章