1. 程式人生 > >noip 模擬賽 T3

noip 模擬賽 T3

mat long long ret math 發現 含義 答案 求解 遞歸

問:如何快速求出等差數列異或和?

玄學題...

對於異或運算,我們可以分開考慮每一位是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