莫隊學習筆記

\(\text{莫隊是一種離線算法。}\)php

\(\text{莫隊 = 分塊 + 暴力}\)html

借用的內容
https://blog.csdn.net/u011815404/article/details/88317786
https://www.cnblogs.com/WAMonster/p/10118934.html
https://blog.csdn.net/Enzymii/article/details/77905451
https://blog.csdn.net/wangqianqianya/article/details/89409522
https://www.myblog.link/2016/01/26/MO-s-Algorithm/
https://blog.csdn.net/huayunhualuo/article/details/52153449
https://blog.csdn.net/a1351937368/article/details/78429044
https://blog.csdn.net/qq_38891827/article/details/82190013
https://blog.csdn.net/chenxiaoran666/article/details/81253315
https://blog.csdn.net/chenxiaoran666/article/details/81251960
https://blog.csdn.net/Runner__/article/details/51398047

通常的區間問題均可以使用莫隊。
\(\text{莫隊的靈魂在於:若是你知道了[L,R]的答案。你能夠在O(1)的時間下獲得[L,R-1]和[L,R+1]和[L-1,R]和[L+1,R]的答案的話。就能夠使用莫隊算法。}\)node

\(\text{前置芝士:分塊 , sort , LCA/樹剖(樹上莫隊)}\)c++

\(\text{莫隊就是把全部的詢問先存下來 排完序一個個玩}\)


git

\(\text{大概就是這個樣子:若是一段區間是l - r的 那麼左指針仍是留在l 右指針是留在r的}\)
\(\text{對於下一次操做:莫隊會把 上一次左邊的位置 移到這一次的位置 右邊也同樣}\)
\(\text{這樣的話 對於樸素暴力已經有了足夠的優化 可是仍是很慢 最壞狀況仍是(N*M)的}\)
\(\text{咱們考慮排序:把這個按左端點排序 左端點相同時按右端點排序 這樣的話是能夠證實的優化 由於左端點只會往右}\)
\(\text{對於一種排序我不會證實其複雜度 不過好像真的快不少呢}\)

(轉自https://blog.csdn.net/u011815404/article/details/88317786)算法

int l=1,r=0,ans=0;
for(int i=1;i<=m;i++){
    while(l>q[i].l) add(--l);//[l-1,r]
    while(l<q[i].l) del(l++);//[l+1,r]
    while(r<q[i].r) add(++r);//[l,r+1]
    while(r>q[i].r) del(r--);//[l,r-1]
    res[q[i].id]=ans;//存儲答案
}

這是離線莫隊的裸的板子(真的就這麼短的四句話只是add和del裏面要加內容。。)優化

至於塊的大小在這兒spa

對於複雜度的分析
\(\text{對於左指針:咱們考慮最壞狀況:莫隊的添加刪除是O(1)的 那麼處理塊i的最壞複雜度是$O(x_i \sqrt(n))$}\)
\(\sum_{i = 1}^{n} x_i * \sqrt{n} + n * \sqrt{n}\) = \(O(n\sqrt{n})\)
\(\text{對於右指針:若是咱們按照右端點排序 最壞狀況顯然是O(n)的即從1跳到n}\)
由此能夠推出 莫隊的複雜度大概就是一個 \(\theta(n \sqrt{n})\)
莫隊代碼都很短的 只要別把 L++ 寫成 ++L.net

例題整理
https://www.lydsy.com/JudgeOnline/problem.php?id=2038 小Z的襪子
https://www.lydsy.com/JudgeOnline/problem.php?id=4540
https://www.luogu.org/problem/P1494 小Z的襪子(雙倍經驗)
http://codeforces.com/contest/617/problem/E
https://www.spoj.com/problems/DQUERY/en/
http://codeforces.com/problemset/problem/86/D
http://acm.hdu.edu.cn/showproblem.php?pid=5213
http://acm.hdu.edu.cn/showproblem.php?pid=5381
http://acm.fzu.edu.cn/problem.php?pid=2226
http://acm.hdu.edu.cn/showproblem.php?pid=4638
http://acm.hdu.edu.cn/showproblem.php?pid=4676
帶修改的莫隊
https://www.luogu.org/problem/P1903
離散化
https://www.lydsy.com/JudgeOnline/problem.php?id=3289
樹上莫隊
https://www.lydsy.com/JudgeOnline/problem.php?id=1086

bzoj4866
bzoj3809 還有在衢州欠下的題(小聲指針

莫隊的板子:https://blog.csdn.net/wangqianqianya/article/details/89409522
例題先鴿 明天再更

https://www.lydsy.com/JudgeOnline/problem.php?id=2038
https://www.luogu.org/problem/P1494

這題好像就是個結論題

數學計算方法爲

對於一個區間的計算咱們設\(i\)的個數爲\(s_i\)
那麼答案就是

$\sum_{i = 1}^{n} {s_i * (s_i -1)} - (r - l + 1) $
\(-------------------\)
${ (r - l) * (r - l + 1) } $

代碼

#include <bits/stdc++.h>
using namespace std ;
#define int long long
inline int read() { register int res = 0 ; int f = 1 ;register char c = getchar() ;
    for( ; !isdigit(c) ; c = getchar()) if(c == '-') f = -1 ;
    for( ; isdigit(c) ; c = getchar()) res = (res << 1) + (res << 3) + (c & 15) ;
    return res * f ;
}
struct node {
    int l , r ;
    int id ;
} ;
// #define int long long

struct Answer {
    int x , y ;
    inline int gcd(int x , int y) {
        return y == 0 ? x : gcd(y , x % y) ;
    }
    inline void Solve() {
        if(! x) {
            x = 0 ; y = 1 ;
        }
        else {
            int g = gcd(x , y) ;
            x /= g ;
            y /= g ;
        }
        printf("%lld/%lld\n" , x , y) ;
        return ;
    }
};

const static int N = 100000 + 5 ;
int n ;
int a[N] ;
node q[N] ;
int bl[N] ;
Answer Ans[N] ;
inline bool cmp(node x , node y) {
    return bl[x.l] ^ bl[y.l] ? x.r < y.r : x.l < y.l ;
}
int ans = 0 ;
int s[N] ;
inline void Delete(int x) {
    ans -= s[a[x]] * s[a[x]] ;
    s[a[x]] -- ;
    ans += s[a[x]] * s[a[x]] ;
}
inline void Insert(int x) {
    ans -= s[a[x]] * s[a[x]] ;
    s[a[x]] ++ ;
    ans += s[a[x]] * s[a[x]] ;
}
signed main() {
    n = read() ; int m = read() ; int unt = sqrt(n) ;
    for(register int i = 1 ; i <= n ; i ++) a[i] = read() ;
    for(register int i = 1 ; i <= n ; i ++) bl[i] = (i - 1) / unt + 1 ;
    for(register int i = 1 ; i <= m ; i ++) {
        int l = read() ;
        int r = read() ;
        q[i] = { l , r , i} ;
    }
    sort(q + 1 , q + m + 1 , cmp) ;
    int l = 1 , r = 0 ;
    int ans_x = 0 ;
    int ans_y = 0 ;
    for(register int i = 1 ; i <= m ; i ++) {
        for( ; l < q[i].l ; Delete(l ++)) ;
        for( ; l > q[i].l ; Insert(-- l)) ;
        for( ; r < q[i].r ; Insert(++ r)) ;
        for( ; r > q[i].r ; Delete(r --)) ;
        if(q[i].l == q[i].r) {
            Ans[q[i].id] = {0 , 1} ;
            continue ;
        }
        ans_x = ans - (q[i].r - q[i].l + 1) ;
        ans_y = (q[i].r - q[i].l + 1) * (q[i].r - q[i].l) ;
        Ans[q[i].id] = {ans_x , ans_y} ;
    }
    for(register int i = 1 ; i <= m ; i ++) {
        Ans[i].Solve() ;
    }
    return 0 ;
}
相關文章
相關標籤/搜索