1. 程式人生 > 實用技巧 >根號分治

根號分治

根號分治

根號演算法——不只是分塊

適用型別:長度為\(n\)的序列,\(m\)個詢問,\(n和\)\(m\)通常同階,顯然的方法有\(O(n^2)\)預處理,\(O(1)\)回答,一種是不預處理,

\(O(n)\)回答\(m\)個詢問,根號分治可以做到\(O((n+m)\sqrt n)\)

luogu P3396 雜湊衝突

\(k\)開始,每隔\(p\)個數取一個數,求它們的和

for(i=k;i<=n;i+=p) ans += value[i];

令答案為\(ans[p][k]\),模數是\(p\),餘數是\(k\),對第\(i\)個數,處理它對ans貢獻

for(p = 1;p <= n;p++) ans[p][i % p] += val[i];
#include<cstdio>
#include<cmath>
using namespace std;
const int S = 150003,N = 403;
int a[S],f[N][N];//f[i][j]模數是i餘數是j 
int n,m,p,x,y; char e[2];
inline int read(){
	int x=0; char c=std::getchar();
	while(c<'0'||c>'9')c=std::getchar();
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=std::getchar();}
	return x;}
int main(){
	n = read(); m = read(); p = sqrt(n);//pow(n,0.33)更快
	for(int i = 1;i <= n;i++){
		a[i] = read();
		for(int j = 1;j <= p;j++)
			f[j][i % j] += a[i];
	}
	for(int i = 1;i <= m;i++){
		scanf("%s",e);x = read(); y = read();
		if(e[0] == 'A'){
			if(x <= p) printf("%d\n",f[x][y]);
			else{
				int az = 0,j;
				for(az,j = y;j <= n;j += x)
				az += a[j];
				printf("%d\n",az);
			}
		}
		else{
			for(int j = 1;j <= p;j++)
				f[j][x % j] += y-a[x];
			a[x] = y;
		}
	}
}