gaga999
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 根號算法 --- # 陣列分塊 ---- - 通常是區間詢問類問題 - 將陣列分成 B 塊處理,共有 $\left\lfloor\dfrac{N}{B}\right\rfloor$ 塊 - 每一塊的答案先預處理好 - 對於詢問,完整包含的塊用預處理的答案 - 左右剩下不完整的暴力處理,詢問也是一樣 - 可能在同一塊要用到 lazy tag - 每一次處理最多 $\left\lfloor\dfrac{N}{B}\right\rfloor$ 塊,剩下最多 2B 個 - 若將 B 設為 $\sqrt{N}$ ,一次操作複雜度是 $O(\sqrt{N})$ ---- ## [LOJ -- 数列分块入门 4](https://loj.ac/p/6280) 給定一個長度 $n$ 的序列,接下來有 $q$ 筆詢問,詢問有兩種 1. 將區間[l,r]的值加上 w 2. 詢問區間[l,r]的和mod c $1\leq n,q\leq 10^5$ - 假裝你不知道線段樹BIT之類的資料結構 ---- - 將陣列分成 $B=\sqrt{N}$ 塊 - 每塊儲存總和與加值的 tag - 每個節點儲存值以及所屬的塊 - 修改時塊的部分更改總和與 tag - 修改時節點更改值與所屬塊總和 - 詢問時塊直接用其總和 - 詢問時節點用值加所屬塊tag ---- 程式碼: ```cpp! #include <cmath> #include <iostream> using namespace std; int id[50005], len; // id 表示块的编号, len=sqrt(n) , 即上述题解中的s, sqrt的时候时间复杂度最优 long long a[50005], b[50005], s[50005]; // a 数组表示数据数组, b 数组记录每个块的整体赋值情况, 类似于 lazy_tag, s // 表示块内元素总和 void add(int l, int r, long long x) { // 区间加法 int sid = id[l], eid = id[r]; if (sid == eid) { // 在一个块中 for (int i = l; i <= r; i++) a[i] += x, s[sid] += x; return; } for (int i = l; id[i] == sid; i++) a[i] += x, s[sid] += x; for (int i = sid + 1; i < eid; i++) b[i] += x, s[i] += len * x; // 更新区间和数组(完整的块) for (int i = r; id[i] == eid; i--) a[i] += x, s[eid] += x; // 以上两行不完整的块直接简单求和,就OK } long long query(int l, int r, long long p) { // 区间查询 int sid = id[l], eid = id[r]; long long ans = 0; if (sid == eid) { // 在一个块里直接暴力求和 for (int i = l; i <= r; i++) ans = (ans + a[i] + b[sid]) % p; return ans; } for (int i = l; id[i] == sid; i++) ans = (ans + a[i] + b[sid]) % p; for (int i = sid + 1; i < eid; i++) ans = (ans + s[i]) % p; for (int i = r; id[i] == eid; i--) ans = (ans + a[i] + b[eid]) % p; // 和上面的区间修改是一个道理 return ans; } int main() { int n; cin >> n; len = sqrt(n); // 均值不等式可知复杂度最优为根号n for (int i = 1; i <= n; i++) { // 题面要求 cin >> a[i]; id[i] = (i - 1) / len + 1; s[id[i]] += a[i]; } for (int i = 1; i <= n; i++) { int op, l, r, c; cin >> op >> l >> r >> c; if (op == 0) add(l, r, c); else cout << query(l, r, c + 1) << endl; } return 0; } ``` ---- ### 我們有樹狀的資料結構,可以處理 RMQ 問題,為什麼需要分塊? ---- ## [Luogu -- 教主的魔法](https://www.luogu.com.cn/problem/P2801) 給定一個長度為 $n$ 的序列,每個位置有初始值 $a_i$ ,接下來有 $q$ 比操作,操作有兩種類型 1. 將區間 $[l,r]$ 的值加上 $w$ 2. 求區間 $[l,r]$ 內有多少個數字大於等於 $w$ $1\leq n\leq 10^6,1\leq q\leq 3000$ ---- - 我們預處理的區間需要排序 - 詢問就可以用二分搜求數量 - 線段樹無法合併兩個序列 - 分塊不需要合併,可以獨立計算 - 詢問不需合併,預處理無法合併用陣列分塊 - 無法用線段樹可以直接套套看 - 塊的大小接近 $\sqrt{n}$ 即可,可以自己測最優的 ---- - 對於每個點,存所屬塊與該點權值 - 對於一塊,存排序好陣列以及加值tag - 修改整塊改tag的偏移值即可,順序不變 - 修改點更改所屬塊排序以及點值 - 詢問塊二分搜第一個$\geq w-tag_i$ - 單點求點權 $a_i+tag_{a_i}\geq w$ - 每次修改 $\sqrt{n}$ 塊,兩塊最後重新排序 - 複雜度 $O(q(\sqrt{n}+\sqrt{n}\log\sqrt{n}))$ ---- 程式碼: ```cpp! #include <bits/stdc++.h> #define lowbit(x) ((x) & -(x)) #define ml(a, b) ((1ll * (a) * (b)) % M) #define tml(a, b) (a) = ((1ll * (a) * (b)) % M) #define ad(a, b) ((0ll + (a) + (b)) % M) #define tad(a, b) (a) = ((0ll + (a) + (b)) % M) #define mi(a, b) ((0ll + M + (a) - (b)) % M) #define tmi(a, b) (a) = ((0ll + M + (a) - (b)) % M) #define tmin(a, b) (a) = min((a), (b)) #define tmax(a, b) (a) = max((a), (b)) #define iter(a) (a).begin(), (a).end() #define riter(a) (a).rbegin(), (a).rend() #define init(a, b) memset((a), (b), sizeof(a)) #define cpy(a, b) memcpy((a), (b), sizeof(a)) #define uni(a) a.resize(unique(iter(a)) - a.begin()) #define pb emplace_back #define mpr make_pair #define ls(i) ((i) << 1) #define rs(i) ((i) << 1 | 1) #define INF 0x3f3f3f3f #define NIF 0xc0c0c0c0 #define eps 1e-9 #define F first #define S second #define AC cin.tie(0)->sync_with_stdio(0) using namespace std; typedef long long llt; typedef pair<int, int> pii; typedef pair<double, double> pdd; typedef pair<llt, llt> pll; typedef complex<double> cd; const int N = 1e6 + 6, B = 400; int ar[N], fr[N], tg[N]; vector<int> st[N]; signed main() { int n, q, l, r, w; char c[5]; scanf("%d%d", &n, &q); for (int i = 1; i <= n; i++) scanf("%d", &ar[i]), st[fr[i] = i / B].pb(ar[i]); for (int i = 0; i < fr[n]; i++) sort(iter(st[i])); while (q-- && scanf("%s%d%d%d", c, &l, &r, &w)) { if (c[0] == 'M') { for (int i = fr[l] + 1; i < fr[r]; i++) tg[i] += w; vector<int> po; if (fr[l] != fr[r]) { for (int i = l; fr[i] == fr[l]; i++) po.pb(lower_bound(iter(st[fr[i]]), ar[i]) - st[fr[i]].begin()); for (int i = l; fr[i] == fr[l]; i++) st[fr[i]][po[i - l]] = ar[i] += w; po.clear(); for (int i = r; fr[i] == fr[r]; i--) po.pb(lower_bound(iter(st[fr[i]]), ar[i]) - st[fr[i]].begin()); for (int i = r; fr[i] == fr[r]; i--) st[fr[i]][po[r - i]] = ar[i] += w; sort(iter(st[fr[l]])), sort(iter(st[fr[r]])); } else { for (int i = l; i <= r; i++) po.pb(lower_bound(iter(st[fr[i]]), ar[i]) - st[fr[i]].begin()); for (int i = l; i <= r; i++) st[fr[i]][po[i - l]] = ar[i] += w; sort(iter(st[fr[l]])); } } else { int ans = 0; for (int i = fr[l] + 1; i < fr[r]; i++) ans += st[i].end() - lower_bound(iter(st[i]), w - tg[i]); if (fr[l] != fr[r]) { for (int i = l; fr[i] == fr[l]; i++) ans += (ar[i] + tg[fr[i]] >= w); for (int i = r; fr[i] == fr[r]; i--) ans += (ar[i] + tg[fr[i]] >= w); } else { for (int i = l; i <= r; i++) ans += (ar[i] + tg[fr[i]] >= w); } printf("%d\n", ans); } } } ``` --- # 莫隊演算法 ---- - 可以輕易將狀態加入一個點或刪除一個點 - 需要先讀入所有詢問,用更好的順序處理 - 一樣會有塊的大小B,用來輔助排序詢問 - 將詢問以左界所屬塊排序,同塊以右界排序 - 同塊左界每次最多移動 B,右界總共移動 N - 左界持續往右總共 N ,右界移動$\left\lfloor\dfrac{N}{B}\right\rfloor$ 次 - 設 $B=\sqrt{N}$ ,總複雜度 $O(n\sqrt{n})$ ---- ### 奇偶優化 - 設有 $k$ 塊,右界動$N$最多往右 k 次,左 k-1 次 - 當塊編號是奇數,右界降序排,偶數時升序排 - 往左往右交替,不會出現每塊開始時,需要要返回到前面,均攤每塊右界最多移動 $n$ - 常數優化不到兩倍,但分塊常常需要壓時間 ---- ## [ZJ -- 區間眾數](https://zerojudge.tw/ShowProblem?problemid=b417) 給定一個長度 $n$ 序列 $a_i$ ,接下來有 $q$ 比詢問,每筆詢問求區間 $[l,r]$ 內,同個數字出現最多幾次,有多少種數字出現最多次? $1\leq n,\leq 10^5,1\leq q\leq 10^6$ ---- - 當前狀態維護數字出現次數,以及各次數有幾種數字 - 用一塊大小 $\sqrt{n}$ 左右排序詢問 - 每次插入刪除數字,更改出現次數與種類 - 第一次加入最大次數,更新最大次數 - 最大次數扣到 0 次,最大次數-1(減少一次) - 要先插入再刪除,避免刪到未加入的數字壞掉 ---- 程式碼: ```cpp! #include <bits/stdc++.h> #define lowbit(x) ((x) & -(x)) #define ml(a, b) ((1ll * (a) * (b)) % M) #define tml(a, b) (a) = ((1ll * (a) * (b)) % M) #define ad(a, b) ((0ll + (a) + (b)) % M) #define tad(a, b) (a) = ((0ll + (a) + (b)) % M) #define mi(a, b) ((0ll + M + (a) - (b)) % M) #define tmi(a, b) (a) = ((0ll + M + (a) - (b)) % M) #define tmin(a, b) (a) = min((a), (b)) #define tmax(a, b) (a) = max((a), (b)) #define iter(a) (a).begin(), (a).end() #define riter(a) (a).rbegin(), (a).rend() #define init(a, b) memset((a), (b), sizeof(a)) #define cpy(a, b) memcpy((a), (b), sizeof(a)) #define uni(a) a.resize(unique(iter(a)) - a.begin()) #define pb emplace_back #define mpr make_pair #define ls(i) ((i) << 1) #define rs(i) ((i) << 1 | 1) #define INF 0x3f3f3f3f #define NIF 0xc0c0c0c0 #define eps 1e-9 #define F first #define S second #define AC cin.tie(0)->sync_with_stdio(0) using namespace std; typedef long long llt; typedef pair<int, int> pii; typedef pair<double, double> pdd; typedef pair<llt, llt> pll; typedef complex<double> cd; const int N = 1e5 + 5, M = 1e6 + 5, B = 150; int ar[N], ct[N], an[N], a1[M], a2[M], mx, ql[M], qr[M], od[M]; bool cmp(int a, int b) { const int x = ql[a] / B, y = ql[b] / B; if (x != y) return x < y; return (qr[a] < qr[b]) ^ (x & 1); } void add(int x) { an[ct[x]]--, an[++ct[x]]++, tmax(mx, ct[x]); } void sub(int x) { if (!--an[ct[x]] && ct[x] == mx) mx--; an[--ct[x]]++; } signed main() { int n, q; scanf("%d%d", &n, &q); for (int i = 1; i <= n; i++) scanf("%d", &ar[i]); for (int i = 0; i < q; i++) scanf("%d%d", &ql[i], &qr[i]); iota(od, od + q, 0); sort(od, od + q, cmp); for (int l = 1, r = 0, i = 0; i < q; i++) { while (r < qr[od[i]]) add(ar[++r]); while (l > ql[od[i]]) add(ar[--l]); while (r > qr[od[i]]) sub(ar[r--]); while (l < ql[od[i]]) sub(ar[l++]); a1[od[i]] = mx, a2[od[i]] = an[mx]; } for (int i = 0; i < q; i++) printf("%d %d\n", a1[i], a2[i]); } ``` ---- ## [Luogu -- [国家集训队] 小 Z 的袜子](https://www.luogu.com.cn/problem/P1494) 給定一個長度 $n$ 的序列 $a_i$ ,接下來有 $q$ 比詢問,每筆詢問求在區間 $[l,r]$ 內,隨機選取兩個不同位置,請問兩個位置數字相同機率是多少? $1\leq n,m\leq 10^5$ ---- - 維護當前區間個數字出現次數 $c_i$ - 答案會是 $(\sum_{pos_i\in [l,r]}{\binom{c_i}{2}})/\binom{r-l+1}{2}$ - 更新 $c_i$ 時扣掉選舊的,加上選新的 - 觀察到一般莫隊無法進行修改操作 ---- 程式碼: ```cpp! #include <bits/stdc++.h> #define lowbit(x) ((x) & -(x)) #define tmin(a, b) (a) = min((a), (b)) #define tmax(a, b) (a) = max((a), (b)) #define iter(a) (a).begin(), (a).end() #define riter(a) (a).rbegin(), (a).rend() #define init(a, b) memset((a), (b), sizeof(a)) #define cpy(a, b) memcpy((a), (b), sizeof(a)) #define uni(a) a.resize(unique(iter(a)) - a.begin()) #define pb push_back #define mpr make_pair #define ls(i) ((i) << 1) #define rs(i) ((i) << 1 | 1) #define INF 0x3f3f3f3f #define eps 1e-9 #define F first #define S second #define N 50004 #define AC cin.tie(0)->sync_with_stdio(0) using namespace std; typedef long long llt; typedef pair<int, int> pii; typedef pair<double, double> pdd; typedef pair<llt, llt> pll; typedef complex<double> cd; #define f(i) (((i)*llt((i)-1)) >> 1) int lm, ar[N], ct[N]; llt a[N], b[N], cr; struct P { int l, r, id; bool operator<(const P &rh) const { if (l / lm == rh.l / lm) return (r < rh.r) ^ ((l / lm) & 1); return l / lm < rh.l / lm; } } qy[N]; inline void add(int x) { cr -= f(ct[x]); ct[x]++; cr += f(ct[x]); } inline void sub(int x) { cr -= f(ct[x]); ct[x]--; cr += f(ct[x]); } signed main() { int n, q; scanf("%d%d", &n, &q); for (int i = 1; i <= n; i++) scanf("%d", &ar[i]); lm = sqrt(n); for (int i = 0; i < q; i++) scanf("%d%d", &qy[i].l, &qy[i].r), qy[i].id = i; sort(qy, qy + q); for (int i = 0, l = 1, r = 0; i < q; i++) { while (r < qy[i].r) add(ar[++r]); while (l > qy[i].l) add(ar[--l]); while (r > qy[i].r) sub(ar[r--]); while (l < qy[i].l) sub(ar[l++]); a[qy[i].id] = cr; b[qy[i].id] = f(r - l + 1); } for (int i = 0; i < q; i++) { if (a[i]) { cr = __gcd(a[i], b[i]); printf("%lld/%lld\n", a[i] / cr, b[i] / cr); } else puts("0/1"); } } ``` ---- ### 可修改莫隊 - 對於一個詢問,有三維屬性$(l,r,t)$ - $t$ 帶表進行了多少次修改操作 - 移動到相鄰位置,只需要花很少時間O(1) - 移動 t 軸時,將陣列的特定位置更改 - 若修改位置在區間內,刪除舊的值加上新的 - 不能處理區間修改的問題 ---- - 將塊的大小設 $B$ ,總共 $n/B$ 塊,修改操作 $t$ 次 - 詢問以(左界塊號碼, 右界塊號碼 , 修改次數)排序 - 時間軸移動 $\dfrac{n^2}{B^2}t$ ,同塊 l,r 移動 $QB$ - 右界跨塊移動 $\dfrac{n^2}{B}$,左界跨塊移動 $n$ - 假設 $n=t=Q$ ,取 $B=n^{2/3}$ 最佳 - 複雜度為 $O(n^{5/3})$,比不修改的略差一些 - 操作簡單,記得排序方法和塊大小(較大)即可 ---- ## [luogu -- 数颜色 / 维护队列](https://www.luogu.com.cn/problem/P1903) 給一個長度 $n$ 的序列 $a_i$ ,接下來有 $q$ 次操作,操作有兩種 1. 將第 $p$ 個數字改成 $w$ 2. 求區間 $[l,r]$ 內有多少種不同數字 $1\leq n,q\leq 133333,1\leq a_i,w\leq 10^6$ ---- - 當前狀態維護各數字出現次數與種類數量 - 幫詢問與修改分開編號 - 紀錄每次詢問前有幾次修改 - 紀錄每次修改位置與修改前後的值 - 用上述方法分塊大小進行排序 - 移動 $(l,r,t)$ 並更新序列算答案 ---- 程式碼: ```cpp! #include <bits/stdc++.h> #define tmin(a, b) (a) = min((a), (b)) #define tmax(a, b) (a) = max((a), (b)) #define iter(a) (a).begin(), (a).end() #define riter(a) (a).rbegin(), (a).rend() #define init(a, b) memset((a), (b), sizeof(a)) #define cpy(a, b) memcpy((a), (b), sizeof(a)) #define uni(a) a.resize(unique(iter(a)) - a.begin()) #define pb emplace_back #define mpr make_pair #define ls(i) ((i) << 1) #define rs(i) ((i) << 1 | 1) #define INF 0x3f3f3f3f #define NIF 0xc0c0c0c0 #define eps 1e-9 #define F first #define S second #define AC cin.tie(0)->sync_with_stdio(0) using namespace std; typedef long long llt; typedef pair<int, int> pii; typedef pair<double, double> pdd; typedef pair<llt, llt> pll; typedef complex<double> cd; const int N = 1.4e5, M = 1e6 + 6, B = 5000; struct Q { int l, r, id, t; } qy[N]; int ar[N], mp[N], m1[N], m2[N], cnt, cq, ct[M], ans; bool cmp(const Q &a, const Q &b) { return mpr(mpr(a.l / B, a.r / B), a.t) < mpr(mpr(b.l / B, b.r / B), b.t); } inline void add(int x) { if (!ct[x]++) ans++; } inline void sub(int x) { if (!--ct[x]) ans--; } signed main() { int n, q; char c[5]; scanf("%d%d", &n, &q); for (int i = 1; i <= n; i++) scanf("%d", &ar[i]); for (int i = 0; i < q; i++) { scanf("%s", c); if (c[0] == 'R') { cnt++; scanf("%d%d", &mp[cnt], &m2[cnt]); m1[cnt] = ar[mp[cnt]], ar[mp[cnt]] = m2[cnt]; } else { scanf("%d%d", &qy[cq].l, &qy[cq].r); qy[cq].id = i, qy[cq].t = cnt, cq++; } } for (int i = cnt; i > 0; i--) ar[mp[i]] = m1[i]; sort(qy, qy + cq, cmp); for (int i = 0, l = 1, r = 0, t = 0; i < cq; i++) { Q &qq = qy[i]; while (r < qq.r) add(ar[++r]); while (l > qq.l) add(ar[--l]); while (r > qq.r) sub(ar[r--]); while (l < qq.l) sub(ar[l++]); while (t < qq.t) { t++; if (l <= mp[t] && mp[t] <= r) add(m2[t]), sub(m1[t]); ar[mp[t]] = m2[t]; } while (t > qq.t) { if (l <= mp[t] && mp[t] <= r) add(m1[t]), sub(m2[t]); ar[mp[t]] = m1[t], t--; } qq.t = ans; } sort(qy, qy + cq, [&](const Q &a, const Q &b) { return a.id < b.id; }); for (int i = 0; i < cq; i++) printf("%d\n", qy[i].t); } ``` ---- ### 回滾莫隊 - 當僅有刪除操作好做,插入需要加全部均攤 - 詢問左界分到所在的塊,同一塊一起 O(n) 處理 - 一塊開始時,先插入該塊以右所有節點 - 同塊的詢問依照右界降序排序 - 每次詢問先刪右界的,再刪左界的 - 算好答案以後,左界刪除的操作undo掉 ---- 題目:https://www.luogu.com.cn/problem/P8078 比較難,就不講解了 題解:https://hail-garage-f95.notion.site/P8087-5412fd878b0f4ca6a14925520c2f2c60?pvs=4 --- # 根號分治 - 可能有算式 $x\times y =k\leq 10^6$ - 對於 $x\leq 10^3(\sqrt{n})$,枚舉 x 後O(n) 處理 y - 對於 $x>10^3(\sqrt{n})$,$y\leq\sqrt{n}$,特殊處理 y ---- ## [CF -- D. Tree Queries](https://codeforces.com/contest/1254/problem/D) 給定一棵 $n$ 個節點的樹,初始的點權為 0 ,接下來有 $q$ 比詢問,每筆詢問有兩種 1. 給節點 $v$ ,然後隨機取節點 $r$ ,對於所有的節點 $u$ ,若 $u$ 到 $r$ 的最短路徑經過 $v$ ,將節點權值加上 $d$ 2. 詢問節點 $v$ ,求該節點權值期望值 $1\leq n,q,\leq 2\times 10^5$ ---- - 觀察到若節點 $u$ 被更改,在以 $v$ 為根的樹,$u$ 和 $r$ 屬於不同子樹 - 同一個子樹加值相同,設子樹大小為 $s$,加了 $\frac{n-s}{n}\times d$ - 節點 $v$ 直接加上 $d$ - 可以用樹壓平,對子樹區間修改 - 一個修改對詢問影響,可以 O(logn) 用LCA處理 ---- - 度數超過 $\sqrt{n}$ 的節點數量不超過 $\sqrt{n}$ 個 - 對於修改節點,輕點遍歷鄰邊將子樹用樹壓平更新 - 詢問O(logn)補上每個重點的影響 - 總複雜度 $O(n\sqrt{n}\log n)$ - 重點其實很少,分界可以設小一點更快 ---- 程式碼: ```cpp! #include <bits/stdc++.h> #define lowbit(x) ((x) & -(x)) #define ml(a, b) ((1ll * (a) * (b)) % M) #define tml(a, b) (a) = ((1ll * (a) * (b)) % M) #define ad(a, b) ((0ll + (a) + (b)) % M) #define tad(a, b) (a) = ((0ll + M + (a) + (b)) % M) #define mi(a, b) ((0ll + M + (a) - (b)) % M) #define tmi(a, b) (a) = ((0ll + M + (a) - (b)) % M) #define tmin(a, b) (a) = min((a), (b)) #define tmax(a, b) (a) = max((a), (b)) #define iter(a) (a).begin(), (a).end() #define riter(a) (a).rbegin(), (a).rend() #define init(a, b) memset((a), (b), sizeof(a)) #define cpy(a, b) memcpy((a), (b), sizeof(a)) #define uni(a) a.resize(unique(iter(a)) - a.begin()) #define pb emplace_back #define mpr make_pair #define ls(i) ((i) << 1) #define rs(i) ((i) << 1 | 1) #define INF 0x3f3f3f3f #define NIF 0xc0c0c0c0 #define eps 1e-9 #define F first #define S second #define AC cin.tie(0)->sync_with_stdio(0) using namespace std; typedef long long llt; typedef pair<int, int> pii; typedef pair<double, double> pdd; typedef pair<llt, llt> pll; typedef complex<double> cd; const int M = 998244353; // random_device rm; // mt19937 rg(rm()); // default_random_engine rg(rm()); // uniform_int_distribution<int> rd(INT_MIN, INT_MAX); // uniform_real_distribution<double> rd(0, M_PI); void db() { cerr << "\n"; } template <class T, class... U> void db(T a, U... b) { cerr << a << " ", db(b...); } template <typename T> void rd(T &x) { x = 0; bool f = 0; char c = getchar(); while (!isdigit(c)) f ^= !(c ^ 45), c = getchar(); while (isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getchar(); f && (x = -x); } template <typename T> void prt(T x) { if (x < 0) putchar('-'), x = -x; if (x > 9) prt(x / 10); putchar((x % 10) ^ 48); } const int N = 1.5e5 + 5, B = 150; vector<int> eg[N], sk; int ac[20][N], in[N], ou[N], sz[N], ct; int vl[N], bs[N]; inline int pw(int v, int p) { int sm = 1; for (; p; p >>= 1) { if (p & 1) tml(sm, v); tml(v, v); } return sm; } void dfs(int u, int fa) { in[u] = ++ct, sz[u] = 1; for (int v : eg[u]) { if (v == fa) continue; ac[0][v] = u; for (int t = 1; t < 20; t++) ac[t][v] = ac[t - 1][ac[t - 1][v]]; dfs(v, u), sz[u] += sz[v]; } if (eg[u].size() >= B) sk.pb(u); ou[u] = ct; } inline bool isa(int p, int x) { return in[p] <= in[x] && ou[x] <= ou[p]; } inline void cg(int p, int v) { for (; p < N; p += lowbit(p)) tad(bs[p], v); } inline int qy(int p) { int sm = 0; for (; p; p -= lowbit(p)) tad(sm, bs[p]); return sm; } signed main() { int n, q, x, y, c; rd(n), rd(q); for (int i = 1; i < n; i++) { rd(x), rd(y); eg[x].pb(y); eg[y].pb(x); } dfs(1, -1); int rn = pw(n, M - 2); while (q--) { rd(c); if (c == 1) { rd(x), rd(y); if (!y) continue; if (eg[x].size() < B) { cg(in[1], ml(y, ml(sz[x], rn))); cg(in[x], ml(y, mi(1, ml(sz[x], rn)))); cg(in[x] + 1, -y); cg(ou[x] + 1, ml(y, ml(sz[x], rn))); for (int v : eg[x]) if (v != ac[0][x]) cg(in[v], ml(y, mi(1, ml(sz[v], rn)))), cg(ou[v] + 1, -ml(y, mi(1, ml(sz[v], rn)))); } else tad(vl[x], y); } else { rd(x); int ans = qy(in[x]); for (int i : sk) { if (!vl[i]) continue; if (i == x) tad(ans, vl[i]); else if (isa(i, x)) { int u = x; for (int k = 19; k >= 0; k--) if (ac[k][u] && !isa(ac[k][u], i)) u = ac[k][u]; tad(ans, ml(vl[i], mi(1, ml(sz[u], rn)))); } else tad(ans, ml(vl[i], ml(sz[i], rn))); } prt(ans), putchar('\n'); } } } ``` ---- ## 不會過另解:操作分塊 - 紀錄了一些修改,可以 O(n) dp 更改整張圖 - 將詢問 $\sqrt{n}$ 分成一塊,每塊結束 O(n) 更新圖 - 塊內詢問用LCA枚舉塊內修改影響 - 複雜度一樣,但是常數超級大 [參考 Solution1](https://hail-garage-f95.notion.site/1254D-d05b85923a5240e1b934d0c382219825?pvs=4) ---- --- # 數論分塊 ---- - 遇到要處理下連續下高斯函數時 - 問題形式可能長 $\sum_{i=1}^{n}{f(i)g(\lfloor \frac{n}{i}\rfloor)}$ - 已知 $f$ 前綴,可以快速算 $\sum_{i=l}^{r}{f(i)}$ - 相同的 $g(\lfloor \frac{n}{i}\rfloor)$ 的與 $f(i)$ 打包一起算 - 總共有 $O(\sqrt{n})$ 種 $\lfloor \frac{n}{i}\rfloor$ ---- ![](https://hackmd.io/_uploads/SJA5vLLMT.png) ---- - 對於 $\lfloor\dfrac{n}{i}\rfloor=k$,我們想要找出 $i$ 的左右界 - 可以枚舉左界是上個右界+1,右界剛好是 $\lfloor\dfrac{n}{k}\rfloor$ 1. 固定初始做界為 1 2. 算右界與計算函數值 3. 更新左界為右界+1,重複2 4. 左界 > n 結束 ### [略證](https://hail-garage-f95.notion.site/e9fe227bfb8c45fa8ef34bac2a3f2a54?pvs=4) ---- ## [UVA -- 11526 - H(n)](https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=27&page=show_problem&problem=2521) 總共有 $T$ 比測資,每一筆測資有一個數字 $n$ ,求 $\sum_{i=1}^{n}{\lfloor\dfrac{n}{i}\rfloor}$ $1\leq T\leq 1000,\ 1\leq n\leq 2^{31}$ - 以上面的式子來看, $f(x)=1,\ g(x)=x$ ---- 程式碼: ```cpp! long long H(int n) { long long res = 0; // 储存结果 int l = 1, r; // 块左端点与右端点 while (l <= n) { r = n / (n / l); // 计算当前块的右端点 res += (r - l + 1) * 1LL * (n / l); // 累加这一块的贡献到结果中。乘上 1LL 防止溢出 l = r + 1; // 左端点移到下一块 } return res; } ``` ---- ## [CSES -- Sum of Divisors](https://cses.fi/problemset/task/1082) 定義函數 $H(x)$ 為 $x$ 的因數和,例如 $H(12)=1+2+3+4+6+12=28$,給出一個整數 $n$ ,求 $\sum_{i=1}^{n}{H(i)}\ \ mod\ \ 10^9+7$ $1\leq n\leq 10^{12}$ ---- - 一個數 $x$ 會當因數 $\lfloor\dfrac{n}{x}\rfloor$ 次 - 改寫式子 $\sum_{i=1}^{n}{(i\times\lfloor\frac{n}{i}\rfloor)}$ - 以上面的式子來看, $f(x)=x,\ g(x)=x$ - 不一定要有式子才數論分塊 - 遇到下高斯可能可快速算左右界幫助 dp - 有可能只是除法結果 $O(\sqrt{n})$ 種,幫助離散化 ---- 程式碼: ```cpp! #include <bits/stdc++.h> #define lowbit(x) ((x) & -(x)) #define ml(a, b) ((1ll * (a) * (b)) % M) #define tml(a, b) (a) = ((1ll * (a) * (b)) % M) #define ad(a, b) ((0ll + (a) + (b)) % M) #define tad(a, b) (a) = ((0ll + (a) + (b)) % M) #define mi(a, b) ((0ll + M + (a) - (b)) % M) #define tmi(a, b) (a) = ((0ll + M + (a) - (b)) % M) #define F first #define S second #define AC cin.tie(0)->sync_with_stdio(0) using namespace std; typedef long long llt; typedef pair<int, int> pii; typedef pair<double, double> pdd; typedef pair<llt, llt> pll; typedef complex<double> cd; const int M = 1e9 + 7; inline int C2(llt x) { return (((x + 1) * x) >> 1) % M; } signed main() { llt n, l = 1, r; int ans = 0; scanf("%lld", &n); while (l <= n) { r = n / (n / l); tad(ans, ml(n / l, mi(C2(r % M), C2((l - 1) % M)))); l = r + 1; } printf("%d\n", ans); } ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully