學習筆記——根號分治
阿新 • • 發佈:2021-07-14
根號分治
根號分治與其說是一個演算法,更不如說是一種思想
例題1
給出一個正整數\(x\),和\(n\)個整數\(a_i\),求\(x^{a_i} \bmod p\)
對於\(100\%\)的資料,\(1\leq n\leq 10^6,1\leq x,a_i<p,p=99824435\color{red}2\)
這很顯然可以用二分快速冪來解決,時間複雜度\(O(nlogn)\),但如果時限為\(0.2s\)呢,二分快速冪就有一點乏力了。
因為發現底數\(x\)不變,考慮打表,將\(x^k\)全部預處理出來,最後直接查詢。預處理,時間複雜度\(O(k)\),空間複雜度\(O(k)\),查詢,時間複雜度\(O(1)\)
但將兩種方法結合一下,根號分治,這道題就可以做了:
因為\(x^k = x^{k\%p + p*\lfloor k/p \rfloor} = x^{k\%p} * x^{p*\lfloor k/p \rfloor}\)
那麼\(p\)的取值就至關重要,根號分治其實就是\(p=\sqrt n\),然後分類討論
對於\(k <= \sqrt n\)時,我們可以\(O(\sqrt n)\)預處理\([1,\sqrt n]\)的\(x^k\)
對於\(k>\sqrt n\)
可以先\(O(\sqrt n)\)求出\((1 <= i <= \sqrt n)\)的\(x^{i * \lfloor k/p \rfloor}\)
然後通過\(x^k = x^{k\%p + p*\lfloor k/p \rfloor} = x^{k\%p} * x^{p*\lfloor k/p \rfloor}\)求得最後答案
預處理\(O(\sqrt n)\),查詢\(O(1)\)
\(code:\)
#include<iostream> #include<cstdio> #include<cmath> using namespace std; #define ri register int static char buf[1000000],*p1=buf,*p2=buf,obuf[1000000],*p3=obuf; #define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++ #define putchar(x) (p3-obuf<1000000)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x) template<typename item> inline void read(register item &x) { x=0;register char c=getchar(); while(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar(); } static char cc[10000]; template<typename item> inline void print(register item x) { register long long len=0; while(x)cc[len++]=x%10+'0',x/=10; while(len--)putchar(cc[len]); } typedef long long ll; const ll mod = 998244352; int n,x,a; const int MAX_N = 32000; ll f[MAX_N],ff[MAX_N]; signed main(){ // freopen("P.in","r",stdin); // freopen("P.out","w",stdout); read(x),read(n); f[0]=ff[0]=1; int m=sqrt(mod); for(int i=1;i<=m;i++) f[i]=f[i-1]*x%mod; for(int i=1;i<=m;i++) ff[i]=ff[i-1]*f[m]%mod; for(ri i=1;i<=n;i++){ read(a); print(f[a%m]*ff[a/m]%mod); putchar(' '); } fwrite(obuf,p3-obuf,1,stdout); return 0; }