关于根号分治
根号分治就是暴力+暴力!——recloud
定义
根号分治是一种写题的trick(技巧),只是一种思想,以例题来作讲解
1.CF1207F
区间查询,单点修改。
很容易有两种很直接的暴力思路:
-
单点直接暴力修改:\(O(1)\)+区间暴力查询\(O( \dfrac{5e5}{x} )\)
当x每次为1,总体时间可达\(O(5e5*n)=O(n^2)\)。 -
修改时维护一个\(ans[i][j],\)表示取模\(x\)余\(y\)的总值,每次暴力遍历x,当x为n时,可达\(O(x)=O(n)\)
查询时直接ans输出,\(O(1)\),总体时间为\(O(n^2)\).
欸,两个\(n^2\)
观察一下:,第一个式子的难点在于\(O( \dfrac{5e5}{x} )\),当x过小,查询过复杂
第二个式子在于修改时\(O(x)\)x太大修改太复杂,结合一下?
!
我们预订一个阈值\(b\),\(x\)大于\(b\)时,使用方案一,否则使用方案二
代码如下,本题阈值为700:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int M=5e5+110,N=1e3+110;
inline int read(){
int sum=0,k=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')k=-1;c=getchar();
}while(c>='0'&&c<='9'){sum=sum*10+c-48;c=getchar();
}return sum*k;
}
inline void wr(int x){
if(x<0) putchar('-'),x=-x;
if(x>9) wr(x/10);
putchar(x%10+'0');
}
inline void co(int x){
wr(x);putchar('\n');
}
int q=0,ans=0;
int a[M],res[N][N];
signed main(){
q=read();
while(q--){
int opt=read(),x=read(),y=read();
if(opt==1){
for(int i=1;i<700;i++) res[i][x%i]+=y;
a[x]+=y;
}else{
if(x<700) co(res[x][y]);
else{
for(int i=y;i<=M-110;i+=x) ans+=a[i];
co(ans);ans=0;
}
}
}
return 0;
}
至于为什么阈值为700?
额,根号分治,5e5=500000的范围,\(\sqrt{500000}=707.1067811865476,\)而且: