1. 程式人生 > 其它 >學習筆記——根號分治

學習筆記——根號分治

根號分治

根號分治與其說是一個演算法,更不如說是一種思想

例題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;
}