1. 程式人生 > 其它 >洛谷 P4900 - 食堂(推式子)

洛谷 P4900 - 食堂(推式子)

推式子+下取整的轉化

洛谷題面傳送門

首先推式子:

\[\begin{aligned} ans&=\sum\limits_{i=A}^B\sum\limits_{j=1}^i\{\dfrac{i}{j}\} \end{aligned} \]

考慮差分,設

\[f(n)=\sum\limits_{i=1}^n\sum\limits_{j=1}^i\{\dfrac{i}{j}\} \]

那麼

\[ans=f(B)-f(A-1) \]

考慮如何計算 \(f(n)\)

\[\begin{aligned} f(n)&=\sum\limits_{i=1}^n\sum\limits_{j=1}^i\{\dfrac{i}{j}\}\\ &=\sum\limits_{i=1}^n\sum\limits_{j=1}^i\dfrac{i}{j}-\lfloor\dfrac{i}{j}\rfloor\\ &=\sum\limits_{i=1}^ni·\sum\limits_{j=1}^i\dfrac{1}{j}-\sum\limits_{i=1}^n\sum\limits_{j=1}^n\lfloor\dfrac{i}{j}\rfloor\\ \end{aligned} \]

如果設 \(s_i=\sum\limits_{j=1}^i\dfrac{1}{j}\)

,那麼減號前面的東西可寫作 \(\sum\limits_{i=1}^ni·s_i\),一遍字首和求出。下面著重考慮減號右邊的東西:

\[\begin{aligned} \sum\limits_{i=1}^n\sum\limits_{j=1}^n\lfloor\dfrac{i}{j}\rfloor \end{aligned} \]

然後就是此題一個比較亮眼的地方了,考慮對 \(\lfloor\dfrac{i}{j}\rfloor\) 進行等價轉化,不難發現 \(\lfloor\dfrac{i}{j}\rfloor=\sum\limits_{j\mid k}[k\le i]\),於是乎原式改寫為:

\[\sum\limits_{i=1}^n\sum\limits_{j=1}^n\sum\limits_{k\mid j}[k\le i] \]

考慮每個 \(k\)

會對多少對 \((i,j)\) 產生貢獻,顯然符合條件的 \(i\) 的個數為 \((n+1-i)\)\(j\) 的個數為 \(d(k)\),其中 \(d\) 為約數個數和函式,那麼上式可進一步寫作:

\[\sum\limits_{k=1}^n(n+1-k)·d(k) \]

維護 \(d(k),k·d(k)\) 的字首和即可快速計算上式。

如果您比較勤快使用線性篩求解 \(d\) 那麼時間複雜度為 \(\mathcal O(n)\),而我比較懶所以直接調和級數列舉,複雜度 \(n\log n\)

using namespace fastio;
const int MAXN=1e6;
int inv[MAXN+5],s[MAXN+5],ss[MAXN+5],d[MAXN+5],sd[MAXN+5],ssd[MAXN+5];
void init(){
	for(int i=(inv[0]=inv[1]=1)+1;i<=MAXN;i++) inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<=MAXN;i++) s[i]=(s[i-1]+inv[i])%MOD,ss[i]=(ss[i-1]+1ll*s[i]*i)%MOD;
	for(int i=1;i<=MAXN;i++) for(int j=i;j<=MAXN;j+=i) d[j]++;
	for(int i=1;i<=MAXN;i++) sd[i]=(sd[i-1]+d[i])%MOD,ssd[i]=(ssd[i-1]+1ll*i*d[i])%MOD;
}
int calc(int x){return (ss[x]-(1ll*(x+1)*sd[x]%MOD-ssd[x]+MOD)%MOD+MOD)%MOD;}
int main(){
	init();int qu;read(qu);
	while(qu--){
		int l,r;read(l);read(r);
		printf("%d\n",(calc(r)-calc(l-1)+MOD)%MOD);
	}
	return 0;
}