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\n",ans); return 0; }
noip 模擬賽 T3