日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

【SP26073】DIVCNT1 - Counting Divisors 题解

發(fā)布時間:2023/12/10 c/c++ 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【SP26073】DIVCNT1 - Counting Divisors 题解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

題目描述

定義 \(d(n)\)\(n\) 的正因數(shù)的個數(shù),比如 \(d(2) = 2, d(6) = 4\)

令 $ S_1(n) = \sum_{i=1}^n d(i) $

給定 \(n\),求 \(S_1(n)\)

輸入格式

第一行包含一個正整數(shù) \(T\) (\(T \leq 10^5\)),表示數(shù)據(jù)組數(shù)。

接下來的 \(T\) 行,每行包含一個正整數(shù) \(n\) (\(n < 2^{63}\))。

輸出格式

對于每個 \(n\),輸出一行一個整數(shù),表示 \(S_1(n)\) 的值。

題解

顯然 $ S_1(n) = \sum_{i=1}^n \left \lfloor \frac ni \right \rfloor $
整除分塊可以做到\(O \left( \sqrt n \right)\),這是要死的復(fù)雜度。
但實(shí)際上,這玩意是有個優(yōu)化套路的:
顯然有$ \sum_{i=1}^n \left \lfloor \frac ni \right \rfloor = 2 \sum_{i=1}^{\lfloor \sqrt n \rfloor} \left \lfloor \frac ni \right \rfloor - \left \lfloor \sqrt n \right \rfloor^2 $
那么只需求 \(\sum\limits_{i=1}^{\lfloor \sqrt n \rfloor} \left \lfloor \dfrac ni \right \rfloor\) 的值
它顯然等于 \(f(x) = \dfrac n x\)、直線 \(y = \sqrt n\)\(x\) 軸和 \(y\) 軸所圍成的區(qū)域 \(R\) 中整點(diǎn) (格點(diǎn)) 的個數(shù) (含邊界,\(x\) 軸除外)。我們把它變成了一個區(qū)間數(shù)點(diǎn)問題。

但實(shí)際上這個函數(shù)是個凸函數(shù),因此區(qū)域 \(R\) 的補(bǔ)集 \(R'\) 為一個凸集,我們很顯然的想到用凸包去擬合,然后Pick定理隨便搞一搞。

怎么找凸包呢?我們用SBT大力求斜率,然后弄個單調(diào)棧去搞。
\(B = \left \lfloor \sqrt n \right \rfloor\),我們從點(diǎn) \(P_0 \left( \left \lfloor \dfrac nB \right \rfloor, B+1 \right)\) 開始,一步一步尋找凸包上所有的點(diǎn)。
(步驟一)我們在棧中加入兩個分?jǐn)?shù) (為斜率的絕對值) \(\dfrac 01\)\(\dfrac 11\),代表向量 \((1, 0)\)\((1, 1)\),由于 \(f' \left( \sqrt n \right) = -1\),因此這部分所有斜率都在 \(-1 \sim 0\) 之間。 此外,這個棧需要滿足自頂向上是單調(diào)遞增的。
(步驟二)接下來,我們?nèi)〕鰲m斚蛄?#xff0c;將 \(P_0\) 持續(xù)與這個向量 (關(guān)于 \(x\) 軸的對稱,下略) 相加,直到這個點(diǎn) \(P_k\) 不在區(qū)域 \(R'\) 中 (即在區(qū)域 \(R\) 中)。由于這些分?jǐn)?shù)是在 Stern-Brocot 樹中產(chǎn)生的,因此一定是既約分?jǐn)?shù) (即分子與分母互素)。
因此每加一步,我們可以計算出這個橫條的面積:設(shè)向量為 \((u, v)\),上一個點(diǎn) (\(P_{k-1}\)) 的橫坐標(biāo)為 \(x\),則面積為 \(x v + \dfrac 12 (v + 1) (u - 1)\)
(步驟三)接著我們要對棧進(jìn)行調(diào)整。由于函數(shù)的斜率的絕對值單調(diào)遞減,因此棧中的分?jǐn)?shù)也需要單調(diào)遞減。
故我們需要把值過大的分?jǐn)?shù)彈出棧外,直到棧頂和它下面的元素與 \(P_k\) 相加后,前者在 \(R'\) 外,后者在 \(R'\) 內(nèi)。把這兩個向量記作 \(l\)\(r\)
(步驟四)然后我們在SBT上二分(不是說大力求嘛).
\(l = \dfrac {y_l} {x_l}, r = \dfrac {y_r} {x_r}\) (\(l > r\)),則 \(m = \dfrac {y_m} {x_m} = \dfrac {y_l + y_r} {x_l + x_r}\)。令 \(M = P_k + m\) (即 \(P_k\) 按照向量 \(m\) 平移后的點(diǎn)),如果 \(M\)\(R'\) 中,則將 \(r\) 壓入棧后令 \(r \gets m\),繼續(xù)二分;否則,分以下兩種情況討論(二輪判斷):
一:如果 \(\left| f' \left( x_k + x_m \right) \right| \leq r\) (其中 \(x_k\)\(P_k\) 的橫坐標(biāo))——由 \(f'(x) = - \dfrac n {x^2}\) 可得該條件等價于 \(n x_r \leq (x_k + x_m)^2 y_r\) ——則容易得到如果再迭代下去的話,所有的 \(P_k + m\) 都不會落在 \(R'\) 內(nèi),也就能說已經(jīng)二分完畢了,因此只需保留當(dāng)前的棧重新回到步驟 2 進(jìn)入下一輪迭代。
二:如果 \(\left| f' \left( x_k + x_m \right) \right| > r\),則接下來的向量還有可能落入 \(R'\) 中,因此令 \(l \gets m\) 后繼續(xù)二分。

這個做法的復(fù)雜度上界不超過 \(O \left( n^{1/3} \log n \right)\) ,因?yàn)樾甭手挥胁怀^\(O \left( n^{1/3} \right)\)個。(至于怎么證明文文也不會)。
后期函數(shù)的斜率非常小,都是 \(\dfrac 1k\) 的形式,會退化為 \(O(y)\),因此可以在 \(y \leq \sqrt[3]n\) 的部分中直接暴力計算

Code

#include <bits/stdc++.h> const int N=1000005; typedef long long ll; struct qwq {ll x, y;qwq (ll x0 = 0, ll y0 = 0) : x(x0), y(y0) {}inline qwq operator + (const qwq &B) const {return qwq(x + B.x, y + B.y);} }; ll n; int top; qwq stk[N]; inline void push(qwq x) { stk[++top]=x;} inline void putint(__int128 x) {static char buf[36];if (!x) {putchar(48); return;} int i = 0;for (; x; buf[++i] = x % 10 | 48, x /= 10);for (; i; --i) putchar(buf[i]); }inline bool check(ll x, ll y) {return n < x * y;}inline bool judge(ll x, qwq v) {return (__int128)n * v.x <= (__int128)x * x * v.y;}__int128 S1() {int i, crn = cbrt(n);ll srn = sqrt(n), x = n / srn, y = srn + 1;__int128 ret = 0;qwq L, R, M;push(qwq(1, 0)); push(qwq(1, 1));while(true) {for (L = stk[top--]; check(x + L.x, y - L.y); x += L.x, y -= L.y)ret += x * L.y + (L.y + 1) * (L.x - 1) / 2;if (y <= crn) break;for (R = stk[top]; !check(x + R.x, y - R.y); R = stk[--top]) L = R;for (; M = L + R, 1; )if (check(x + M.x, y - M.y)) push(R = M);else {if (judge(x + M.x, R)) break;L = M;}}for (i = 1; i < y; ++i) ret += n / i;return ret*2-srn*srn; } int main() {int T;for (scanf("%d", &T); T; --T) {scanf("%lld", &n); putint(S1());puts("");}return 0; }

轉(zhuǎn)載于:https://www.cnblogs.com/Syameimaru/p/10197934.html

總結(jié)

以上是生活随笔為你收集整理的【SP26073】DIVCNT1 - Counting Divisors 题解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。