poj3495 Bitwise XOR of Arithmetic Progression 等差數列異或和
阿新 • • 發佈:2019-01-22
題意
就是求等差數列異或和
x表示首項,y表示末項,z表示公差
怎麼做?
等差數列的和,我會啊 !(首項+末項)*項數/2
然而這題並不是這樣的
由於異或的特殊性,於是我們可以一位一位來考慮
我們定義一個函式
對於第i位,最後一位叫第0位,從右往左數
我們要知道的就是
於是我們決定用最簡單粗暴的方式,那就是求和。。
我們現在要求的等價與下面這個式子
顯然地,式子可以變成下面這一個
那麼前面兩個部分,我們可以一開始就算出來,這個沒有關係
那麼問題就只剩下了後面這一個
這麼怎麼求呢?
我們可以把他看做是一個關於
那麼就是問這個函式,在
容易知道,他的影象是這樣的
要注意的是,n這個地方的點是不能取到的
左邊的是沒有模過,右邊是已經模過了的
右邊這個圖顯然有一個特點,那就是截距肯定是小於
於是最diao的地方就來了
我們旋轉座標軸
沒錯,黃色的那個就是了
這個座標軸取的原點是
我們的問題就等價與求這個新座標軸下包含了多少個點
容易知道,這樣做的話,點數既沒有變多,也沒有變少
於是我們考慮討論這條直線
斜率顯然是之前的倒數(感受一下),那麼就得到了
那麼考慮一下截距是什麼
我們知道,下取整後,以
怎麼做呢?
方法也很簡單
就是一個式子:
這個式子比較顯然。。我就不解釋了
然後這裡有一個問題我想了很久,就是我們能不能取
因為這樣子看似沒有任何問題,並且還可以使得我們的區域變得更小,不失為一個好方法
於是我就直接吧上面的式子的
然後發現WA了很久,為什麼呢?
我們考慮一下,如果兩個值是一樣的,那麼就沒有區別
有區別的地方在於,剛剛好在n這個地方上升了1
那麼這個時候,如果你用這個方法構造出來的答案,截距就要加1了,至於為什麼,讀者可以自己想一想
然後我們就可以變成一個子問題了
發現,無論是截距還是斜率,都如我們所期待的,除了同一個數,那就是
如果再觀察一下,發現,a和c是類似與輾轉相出地進行的,於是有複雜度保證,就是log層就結束了
為什麼這個演算法會得到改進呢?
因為我們發現,如果一個直線,他是十分陡峭的,我們可以輕鬆地算出一大片答案,但是如果比較平緩那就很難搞了
於是我們可以通過旋轉座標軸使得這條直線又變得陡峭起來,這樣子又可以一次算出一堆答案了
從而剩下很多複雜度,這個方法可以學習
然後就沒有啦!完結散花
搞了一個晚上,終於把所有疑問搞懂了,感覺很舒服啊
如果還有在哪裡講得有問題,可以留言啊
CODE:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
LL x,y,z;//x是首項 y是不能超過y z是公差
LL f (LL a,LL b,LL c,LL n)//首項 公差 c 項數
{
//printf("%I64d %I64d %I64d %I64d\n",a,b,c,n);
if (n==0) return 0;
LL ans=(b/c)*n*(n-1)/2+(a/c)*n;
ans=ans+f((b*n+a)%c,c,b%c,((b%c)*n+(a%c))/c);
return ans;
}
int main()
{
while (scanf("%I64d%I64d%I64d",&x,&y,&z)!=EOF)
{
LL ans=0;
for (LL u=0;u<32;u++)
ans=ans|((f(x,z,(1LL<<u),(y-x)/z+1)&1)<<u);
printf("%I64d\n",ans);
}
return 0;
}