題解 P1630 求和

2022-03-28 20:29:02 字數 4062 閱讀 7209

題目

發現題解都不夠優雅,就自己來一篇

首先,看清楚了,題目是 \(\sum_^ai^b\) 的餘數 ,而不是 \(\sum_^ab^i\)

( 等比數列求和了解一下 )

畢竟......本蒟蒻一開始就看錯了......

好,進入正題,介於 \(a,b\leq 10^9\) ,暴力就想都不用想了,肯定過不了每一次乘法需要 \(o(b)\) 的時間,加法需要 \(o(a)\) 的時間,外部乙個 \(o(n)\) 的迴圈,總複雜度 \(o(nab)=10^\) ,大概要 \(10^ s\) ,也就是大概 \(31710\) 年吧 逃

首先,有乙個很優秀的方法可以優化乘法,那就是快速冪

如果你要求 \(a^x\) 那麼你就先求出 \(a^\rfloor}\)

然後 \(a^\rfloor}\times a^\rfloor}\) 就完事啦!

遞迴邊界在於當 \(x=1\) 時 \(a^1=a\)

那奇數怎麼辦?我們知道 \(a^4=a^2\times a^2\) ,但 \(a^5\neq a^2\times a^2\) 啊

沒事,你想, \(a^5=a^2\times a^2\times a^1\) 對不對?

同理,\(a^x=a^\rfloor}\times a^\rfloor}\times a\) ( \(x\) 為奇數)

所以,我們可以遞迴地盤它了:

int pow(int a,int x)
int pow(int a,int x)
int pow(int a,int x)

return ans;

}

好的,我們可喜的發現,由於每次遞迴都是對半,求 \(i^b\) 的方法優化到了 \(o(\log b)\),現在的總複雜度為 \(o(na\log b)\) 了,大概為 \(10^\) 大概要 \(10^4s\) 了,也就是 \(3\) 小時 再逃

這說明我們還是不夠優雅的,我們還得繼續優化

我們還能發現,根據同餘的性質: \((a+10000)\equiv a (\mod 10000)\)

兩邊同時翻 \(b\) 次方得:

\((a+10000)^b\equiv a^b(\mod10000)\)

這說明了啥?說明我們對於任何大於 \(10000\) 的數 \(n\) ,它的 \(b\) 次方我們已經求過了,就是 \((n\%10000)^b\)

因為本人是 c++ 選手,所以習慣用 \(a\%b\) 表示 \(a\) 除以 \(b\) 的餘數

好的,所以我們把 \(1\) ~ \(10000\) 的 \(b\) 次方存起來,比如用 \(c_i\) 表示 \(i^b\%10000\) 。

我們先預處理 \(c_1\)~\(c_\) ,接下來,每次我們要 \(i^b\) ,就直接用 \(c_\) 即可。

耶!總複雜度又下降了,每次預處理都是 \(o(10000\log b)\) 的,統計是 \(o(a)\) 的

\(\because 10000\log b\leq 10^4\times 10^2=10^6\leq 10^9=a\)

\(\therefore o(10000\log b)+o(a)=o(a)\)

總複雜度為 \(o(na)=10^\) , 大概 \(20\) 分鐘吧 繼續逃

至此,我們還能發現,對於給定的 \(a\) ,我們需要將 \(c_1\)~\(c_\) 的加和計算 \(\lfloor\rfloor\) 次,剩下的就是從 \((\lfloor\rfloor\times 10000+1)\) 到 \(a\) 的再各多記一次

也就是把加和乘上 \(\lfloor\rfloor\) ,再加上 \(c_1\)~\(c_\) 的和就可以了

有的機智的小朋友立即意識到了,可以把 \(c_1\)~\(c_\) 的和記錄起來,接下來的乙個迴圈搞掉,豈不美哉

但是為什麼要那麼複雜呢?

我們令 \(add_n=\sum_^nc_i\)

那麼,我們的答案就應該是 \(add_\times \lfloor\rfloor+add_\)

這個實際上叫做字首和

不是嗎?

所以現在很明確了吧,我們要的是 \(add_n\) ,只要這個一出來,我們就可以根據公式,\(o(1)\) 輸出答案了

那 \(add_n\) 又怎麼求? \(c_n\) 全部算出來然後每次算 \(add_n\) 都一遍掃過去?

複雜度 \(o(n^2)\) 的事情過於暴力了吧

我們想:

\(add_n=\sum_^nc_i=\sum_^c_i+c_n=add_+c_n\)

所以我們只要一遍算 \(c_n\) 的時候統計 \(add_n\) 即可

這次,預處理複雜度 \(o(10000\log b)\) ,輸出答案是 \(o(1)\) 的,總複雜度 \(o(10000n\log b)=10^7\) 可以做到一秒出結果了

這邊講乙個毫無意義的高階,雖然對複雜度和答案毫無影響,但確實看起來更優雅:

由尤拉定理得: \(a^\equiv1(\mod n)\)

對於右上角那個鬼東西,它叫尤拉函式,是指小於 \(n\) 的正整數中,與其互質的數的個數

也有的版本是說小於等於 \(n\) 的正整數中,與其互質的數的個數

關於尤拉函式的性質,你們可以看看我 這篇文章 中講尤拉函式的那一部分,其它的可以跳過

至於尤拉定理,記住它就行 oi是不需要數學證明的

根據公式 \(\varphi(10000)=4000\) 所以,我們不需要算 \(b\) 次方的快速冪,\(b\%4000\) 的快速冪即可

這樣,您的**將看起來更加的優雅、大氣

hrq:「我現在終於知道了,**優雅指的就是別人看不懂!原來我**不優雅是褒義詞!」

(p.s. 雖然絲毫不影響複雜度)

【**】

好的,廢話了那麼多,本蒟蒻要放 我碼風極醜無比的 **了

**前面加了一些讀入和輸出優化,無視即可

#include#includeusing namespace std;

#define f(a,b,c,d) for(register int a=b,c=d;a<=c;a++)

#define g(a,b,c,d) for(register int a=b,c=d;a>=c;a--)

#define file(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);

typedef int i32;

typedef long long int i64;

const int mod=10000;

namespace input

inline i32 read()

}namespace output

inline void print(i32 x)

inline void print(char c)

}using namespace input;

using namespace output;

//前面全是讀入輸出優化,無視它們

inline i32 pow(i32 d_a,i32 d_x)

return d_ans;

}//快速冪

inline i32 work(i32 d_a,i32 d_b);

f(i,1,i,mod) ar_d_add[i]=ar_d_add[i-1]+pow(i,d_b),ar_d_add[i]-=( (ar_d_add[i]>=mod)?mod:0 );

i32 d_ans= d_a/mod%mod * ar_d_add[mod]%mod + ar_d_add[d_a%mod];

return d_ans>=mod?(d_ans-mod):d_ans;

}//計算結果

i32 main()

output();

return 0;

}//對於主函式盡可能剪短的特殊癖好

最後安利一下 本蒟蒻的部落格

洛谷P1630 求和

luogup1630 求和 給定 a,b 求 sum limits mod 10000 多組詢問,a,b le 1e9 字首和 快速冪 如果暴力計算答案,那麼時間複雜度為 o tab 使用快速冪優化,那麼時間複雜度為 o ta log b 考慮這樣乙個式子 i b mod n i mod n b 那...

題解 UVA1630 串摺疊 Folding

題目描述 摺疊的定義如下 乙個字串可以看成它自身的摺疊。記作s s x s 是x x 1 個s連線在一起的串的摺疊。記作x s ssss s x個s 如果a a b b 則ab a b 例如,因為3 a aaa,2 b bb,所以3 a c2 b aaacbb,而2 3 a c 2 b aaacaa...

題解 P2261 CQOI2007 餘數求和

decription 給出兩個數n,k,求 sum k i sample input 10 5 sample output 這題要求餘數的和,考慮和除法有些密不可分的關係,那就化化式子 sum k k i i n k sum k i i 那麼我們考慮用除法分塊求。每乙個塊內的和是等差數列,那麼可以快...