發明者:隊爺莫濤node
一種「優雅的暴力」ios
因善於無限卡常而聞名OI界算法
主要思想也十分簡單:函數
暴力,也用到了排序,分塊。優化
實際的操做方式就是經過改變枚舉區間的順序來達到優化複雜度的目的spa
雖然聽上去很玄學,但卻十分有效.net
複雜度在 \(O(n^{\frac{3}{2}})\) 左右code
通常是將查找區間分紅 \(n^{\frac{1}{2}}\) 塊blog
更優的複雜度能夠進行按塊奇偶排序來實現排序
普通莫隊只支持離線操做,在線操做仍是十分雞肋的
但難不倒咱們OIer
後人又總結出回滾莫隊,帶修莫隊,樹上莫隊等多種方法,還沒學,先咕咕着
要注意枚舉的順序,詳見\(Oi-Wiki\)中關於四個循環問題的討論,主要思路是將區間先擴大再縮小
利用莫隊思想對全部查詢的區間進行分塊排序,順便加上奇偶優化
每增長一個數,它對答案的貢獻等於 \(c^{2} - (c - 1)^{2}\)
每減掉一個數,它對答案的貢獻等於 \((c - 1)^{2} - c^{2}\)
/* Work by: Suzt_ilymics Knowledge: ?? Time: O(??) */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define LL long long using namespace std; const int MAXN = 5e4+5; const int INF = 1; const int mod = 1; struct node{ int l, r, bh; }b[MAXN]; int read(){ int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0' , ch = getchar(); return s * w; } int n, m, k, qrtn; int a[MAXN]; LL Ans[MAXN], ans; int cnt[MAXN]; bool cmp(node x, node y){ return x.l / qrtn == y.l / qrtn ? (x.l / qrtn % 2 == 0 ? x.r > y.r : x.r < y.r) : x.l < y.l; } void add(LL pos){ int x = ++cnt[a[pos]]; ans -= (x - 1) * (x - 1); ans += x * x; } void del(LL pos){ int x = --cnt[a[pos]]; ans -= (x + 1) * (x + 1); ans += x * x; } int main() { n = read(), m = read(), k = read(); for(int i = 1; i <= n; ++i) a[i] = read(); for(int i = 1; i <= m; ++i){ b[i].l = read(), b[i].r = read(); b[i].bh = i; } qrtn = sqrt(n + 0.5); sort(b + 1, b + m + 1, cmp); int l = 1, r = 0; for(int i = 1; i <= m; ++i){ while(l > b[i].l) add(--l); while(r < b[i].r) add(++r); while(l < b[i].l) del(l++); while(r > b[i].r) del(r--); Ans[b[i].bh] = ans; } for(int i = 1; i <= m; ++i){ printf("%lld\n", Ans[i]); } return 0; }
不難發現,全部狀況總數等於 \(len * (len - 1) / 2\),其中 \(len\) 是區間長度
設函數 \(c(x) = x * (x - 1) / 2\) ,
則每加一個元素對其的貢獻爲 \(c(x) - c(x - 1)\)
同理減一個元素的貢獻 \(c(x - 1) - c(x)\)
最後的答案等於分子和分母的比,分子爲每一個元素的貢獻,分母爲 \(c(len)\),注意通分和特判特殊狀況
/* Work by: Suzt_ilymics Knowledge: ?? Time: O(??) */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define LL long long using namespace std; const int MAXN = 5e4+5; const int INF = 1; const int mod = 1; struct node{ int l, r, bh; }b[MAXN]; inline int read(){ int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0' , ch = getchar(); return s * w; } int n, m, qrtn; int a[MAXN]; int cnt[MAXN]; LL Ans[MAXN], ans; LL Ansfm[MAXN]; inline bool cmp(node x, node y){ return x.l / qrtn == y.l / qrtn ? (x.l / qrtn % 2 == 0 ? x.r > y.r : x.r < y.r) : x.l < y.l; } inline LL Gcd(LL x, LL y){ return y == 0 ? x : Gcd(y, x % y); } inline LL c(LL x){ return x * (x - 1) / 2; } inline void del(LL pos){ int x = --cnt[a[pos]]; ans -= c(x + 1); ans += c(x); } inline void add(LL pos){ int x = ++cnt[a[pos]]; ans -= c(x - 1); ans += c(x); } int main() { n = read(), m = read(); for(register int i = 1; i <= n; ++i) a[i] = read(); for(register int i = 1; i <= m; ++i){ b[i].l = read(), b[i].r = read(); b[i].bh = i; } qrtn = sqrt(n + 0.5); sort(b + 1, b + m + 1, cmp); int l = 0, r = -1; for(register int i = 1; i <= m; ++i){ while(l > b[i].l){ l--; add(l); } while(r < b[i].r){ r++; add(r); } while(l < b[i].l){ del(l); l++; } while(r > b[i].r){ del(r); r--; } Ans[b[i].bh] = ans;//fenzi Ansfm[b[i].bh] = c(r - l + 1);//fenmu } // cout<<qrtn<<endl; for(int i = 1; i <= m; ++i){ if(!Ans[i]) printf("0/1\n"); else{ LL gcd = Gcd(Ans[i], Ansfm[i]); printf("%lld/%lld\n", Ans[i] / gcd, Ansfm[i] / gcd); } } return 0; }
用 \(ans\) 來記錄有幾個數相同
若是增長一個元素 \(x\) 後,\(cnt[x] == 2\) ,\(ans++\)
若是刪掉一個元素 \(x\) 後,\(cnt[x] == 1\) ,\(ans--\)
最後統計答案時,\(ans == 1\),爲 \(Yes\),不然爲 \(No\)
/* Work by: Suzt_ilymics Knowledge: ?? Time: O(??) */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define LL long long using namespace std; const int MAXN = 1e5+5; const int INF = 1; const int mod = 1; struct node{ int l, r, bh; }b[MAXN]; int read(){ int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0' , ch = getchar(); return s * w; } int n, m, k, qrtn; int a[MAXN]; LL Ans[MAXN], ans; int cnt[MAXN]; bool cmp(node x, node y){ return x.l / qrtn == y.l / qrtn ? (x.l / qrtn % 2 == 0 ? x.r > y.r : x.r < y.r) : x.l < y.l; } void add(LL pos){ int x = ++cnt[a[pos]]; if(x == 2) ans++; } void del(LL pos){ int x = --cnt[a[pos]]; if(x == 1) ans--; } int main() { n = read(), m = read(); for(int i = 1; i <= n; ++i) a[i] = read(); for(int i = 1; i <= m; ++i){ b[i].l = read(), b[i].r = read(); b[i].bh = i; } qrtn = sqrt(n + 0.5); sort(b + 1, b + m + 1, cmp); int l = 1, r = 0; for(int i = 1; i <= m; ++i){ while(l > b[i].l) add(--l); while(r < b[i].r) add(++r); while(l < b[i].l) del(l++); while(r > b[i].r) del(r--); Ans[b[i].bh] = (ans == 0) ? 1 : 0; } for(int i = 1; i <= m; ++i){ Ans[i] == 1 ? printf("Yes\n") : printf("No\n"); } return 0; }
每加一個數,統計個數,看看有沒有新的貢獻。
注意每加一個數或減一個數都有可能作出貢獻,若是多加了也要將相應的貢獻減掉
沒加任何優化的莫隊,當時第一次寫,寫的比較醜
/* Work by: Suzt_ilymics Knowledge: ?? Time: O(??) */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 1e5+5; const int INF = 1; const int mod = 1; struct node{ int l, r, bh, ans; bool operator < (const node &b) const {return l == b.l ? r < b.r : l < b.l; } }b[MAXN]; int read(){ int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0' , ch = getchar(); return s * w; } int n, m, l = 1, r = 0, ans = 0; int a[MAXN], cnt[MAXN]; bool vis[MAXN]; bool cmp(node x, node y){return x.l == y.l ? x.r < y.r : x.l < y.l; } bool cmp2(node x, node y){return x.bh < y.bh; } int main() { n = read(), m = read(); for(int i = 1; i <= n; ++i) a[i] = read(), a[i] = a[i] > 1e5 ? 0 : a[i]; for(int i = 1; i <= m; ++i) b[i].l = read(), b[i].r = read(), b[i].bh = i; sort(b + 1, b + m + 1, cmp); for(int i = 1; i <= m; ++i){ while(l < b[i].l) { cnt[a[l]]--; l++; if(a[l - 1] == cnt[a[l - 1]] && !vis[a[l - 1]]) ans++, vis[a[l - 1]] = 1; if(a[l - 1] != cnt[a[l - 1]] && vis[a[l - 1]]) ans--, vis[a[l - 1]] = 0; } while(r < b[i].r) { cnt[a[++r]]++; if(a[r] == cnt[a[r]] && !vis[a[r]]) ans++, vis[a[r]] = 1; if(a[r] != cnt[a[r]] && vis[a[r]]) ans--, vis[a[r]] = 0; } while(r > b[i].r){ cnt[a[r]]--; r--; if(a[r + 1] == cnt[a[r + 1]] && !vis[a[r + 1]]) ans++, vis[a[r + 1]] = 1; if(a[r + 1] != cnt[a[r + 1]] && vis[a[r + 1]]) ans--, vis[a[r + 1]] = 0; } b[i].ans = ans; } sort(b + 1, b + m + 1, cmp2); for(int i = 1; i <= m; ++i){ printf("%d\n", b[i].ans); } return 0; }
CF617E XOR and Favorite Number
/* Work by: Suzt_ilymics Knowledge: ?? Time: O(??) */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define LL long long #define int long long using namespace std; const int MAXN = 1e5+5; const int kMax = 1e6+6; const int INF = 1; const int mod = 1; struct node{ int l, r, bh; }b[MAXN]; int n, m, k, qrtn; int a[MAXN]; LL Ans[MAXN], ans; int cnt[kMax << 1]; int read(){ int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0' , ch = getchar(); return s * w; } bool cmp(node x, node y) { return x.l / qrtn == y.l / qrtn ? x.r < y.r : x.l < y.l; } void add(int pos){ ans += (LL)cnt[a[pos] ^ k]; ++cnt[a[pos]]; } void del(int pos){ --cnt[a[pos]]; ans -= (LL)cnt[a[pos] ^ k]; } signed main() { n = read(), m = read(), k = read(); for(int i = 1; i <= n; ++i) a[i] = read(), a[i] ^= a[i - 1]; for(int i = 1; i <= m; ++i){ b[i].l = read(), b[i].r = read(), b[i].bh = i; } qrtn = sqrt(n + 0.5); sort(b + 1, b + m + 1, cmp); int l = 0, r = -1; cnt[0] = 1; for(int i = 1; i <= m; ++i){ while(l > b[i].l) l--, add(l - 1); while(r < b[i].r) r++, add(r); while(l < b[i].l) del(l - 1), l++; while(r > b[i].r) del(r), r--; Ans[b[i].bh] = ans; } for(int i = 1; i <= m; ++i){ printf("%lld\n", Ans[i]); } return 0; }