除法分块学习笔记
\(\\\)
除法分块
求以\(N\)为被除数,在\([0,N]\)的范围内,将所得的商向下取整相同的所有除数区间。
- \(N\in [0,10^9]\)
这个问题其实有\(\text O(\sqrt N)\)的解决方案,即除法分块。
我们先给出做法,在证明正确性和复杂度。
\(\\\)
做法
维护两个变量\(L,R\),代表当前除数区间为闭区间\([L,R]\),\(L\)初始值为\(1\)。
然后在\(L\le N\)时循环进行下面的过程:
- 设\(t=\lfloor\frac{N}{L}\rfloor\)
- 当前答案区间的右端点\(R=\lfloor\frac{N}{t}\rfloor\)
- \(L=R+1\)
\(\\\)
正确性证明
开始时左端点是\(1\)显然是没有问题的,而以后的每一次操作\(L=R+1\),因此,我们只需要证明每次的\(R\)都为正确的即可。
首先\(\lfloor\frac{N}{t}\rfloor\)一定是属于该除数区间的,所以我们只需要证明该数为区间上界。
反证法。设\(X=\lfloor\frac{N}{t}\rfloor\)不是我们想要得到的\(R\),那么至少有\(X+1\)属于答案区间。
于是有\(\lfloor\frac{N}{X+1}\rfloor=t\),因为是下取整,于是有\(N\ge t\times (X+1)\),于是有\(\lfloor\frac{N}{t}\rfloor\ge\big(\lfloor\frac{t\times (X+1)}{t}\rfloor=X+1\big)\)
而根据定义有\(X=\lfloor\frac{N}{t}\rfloor\),于是有\(X\ge X+1\),与事实相悖。
\(\\\)
复杂度证明
分情况讨论。
当所选除数\(\le \sqrt N\) 时,显然这一部分的除数区间不会超过\(\sqrt N\) 个。
当所选除数\(\ge \sqrt N\)时,得到的商\(\le \sqrt N\),商不超过\(\sqrt N\)种,所以除数区间也不会超过\(\sqrt N\)个。
于是总时间复杂度\(\text O(\sqrt N)\)。
\(\\\)
\(\\\)
一类求和式的推导
给出两个数列\(A,B\)和一个整数\(N\),其中\(A\)的下标范围为\([1,N]\),\(B\)的下标范围为\([0,N)\)
定义下表范围为\([1,N]\)的数列\(C\),试求出所有的\(C_i\):\(\begin{align}C_i=\sum_{j=1}^i A_{\lfloor\frac{i}{j}\rfloor}\times B_{i\%j}\end{align}\)
- \(N\in [1,10^5]\)
\(\\\)
\(Part\ 1:\ A_i=1,B_i=i\)
\(\begin{align}C_i=\sum_{j=1}^i A_{\lfloor\frac{i}{j}\rfloor}\times B_{i\%j}=\sum_{j=1}^i A_{\lfloor\frac{i}{j}\rfloor}\times B_{i-\lfloor\frac{i}{j}\rfloor\times j}=\sum_{j=1}^i \big(i-\lfloor\frac{i}{j}\rfloor\times j\big)=\sum_{j=1}^i i-\sum_{j=1}^i \lfloor\frac{i}{j}\rfloor\times j\end{align}\)
左一半求和为\(i^2\)
右一半求和时,考虑对于一个除法分块的区间\([L,R]\),这一段区间累加的答案为\(t\times \frac{(L+R)(R-L+1)}{2}\)
\(\\\)
\(Part\ 2:\ A_i=i,B_i=i\)
\(\begin{align}C_i=\sum_{j=1}^i A_{\lfloor\frac{i}{j}\rfloor}\times B_{i\%j}=\sum_{j=1}^i \lfloor \frac i j\rfloor\times\big(i-\lfloor\frac{i}{j}\rfloor\times j\big)=\sum_{j=1}^i i\times\lfloor \frac i j\rfloor -\sum_{j=1}^i \lfloor\frac{i}{j}\rfloor^2\times j\end{align}\)
同样对一个除法分块的区间\([L,R]\)考虑,左一半对答案的贡献为\(t\times i\times (r-l+1)\)
右一半对答案的贡献为\(t^2\times \frac{(L+R)(R-L+1)}{2}\)
\(\\\)
\(Part\ 3:\ A,B\)随机
\(\begin{align}C_i=\sum_{j=1}^i A_{\lfloor\frac{i}{j}\rfloor}\times B_{i\%j}=\sum_{j=1}^i A_{\lfloor\frac{i}{j}\rfloor}\times B_{i-\lfloor\frac{i}{j}\rfloor\times j}\end{align}\)
考虑一个除法分块的区间\([L,R]\)对答案的贡献为\(\begin{align}\sum_{j=L}^R A_{\lfloor\frac{i}{j}\rfloor}\times B_{i-\lfloor\frac{i}{j}\rfloor\times j}=A_{\lfloor\frac{i}{j}\rfloor}\times \sum_{j=L}^R B_{i-\lfloor\frac{i}{j}\rfloor\times j}\end{align}\)
于是左边的\(A\)数列的答案是固定的,右边的\(B\)数列的答案注意到可以表示成一个数列隔一段取一个数求和的形式。
于是\(N^2\)预处理\(f[i][j]\),表示从第\(i\)个位置开始(含第\(i\)个位置),向前每隔\(j\)个位置取一次的数求和,根据除法分块的定义,\(i-t\times R\)应该为可选择的最靠前的数字。于是对于一个除法分块的区间\([L,R]\),有\(\sum_{j=L}^R B_{i-t\times j}=f[i-t\times L][t]\)
\(\\\)
\(Code\)
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 100100
#define mod 123456789
#define R register
#define gc getchar
using namespace std;
typedef long long ll;
inline ll rd(){
ll x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
ll n,a[N],b[N],c[N],f[N][350];
int main(){
n=rd();
for(R ll i=1;i<=n;++i) a[i]=rd();
for(R ll i=1;i<=n;++i) b[i-1]=rd();
for(R ll j=1;j<=340;++j)
for(R ll i=0;i<=n;++i) f[i][j]=((i>=j?f[i-j][j]:0)+b[i])%mod;
for(R ll i=1;i<=n;++i){
for(R ll j=1;j<=340&&j<=i;++j) (c[i]+=a[i/j]*b[i%j])%=mod;
for(R ll l=341,r;l<=i;l=r+1){
ll t=i/l; r=i/t;
(c[i]+=a[t]*f[i-t*l][t])%=mod;
}
printf("%lld\n",c[i]);
}
return 0;
}