初识莫队——小Z的袜子
以前一直覺得莫隊是多么高大上的一種算法,然而仔細看了下發(fā)現(xiàn)其實并不復(fù)雜,實質(zhì)上就是技巧性的暴力美學(xué)。
在我看來莫隊是一種分塊排序后降低復(fù)雜度的算法,當(dāng)答案可以通過左右端點一個一個移動維護出來的時候就可以使用莫隊了。
比如給你4個區(qū)間
(1, 2)
(99, 100)
(3, 4)
(101, 102)
如果你是傻傻的按照給定的順序去移動左右端點,那就是先計算出(1,2)區(qū)間的值,然后左端點先從1移動到99,右端點從2移動到100,計算完(99,100)區(qū)間內(nèi)的值,又哼哧哼哧的左端點從99移回3,右端點從100移回4,計算完(3,4)后又移動到(101,102),顯然這樣實在是太蠢了。
正常人都應(yīng)該想到,那我把計算順序改成(1,2)(3,4)(99,100)(101,102)不就行了,但是因為區(qū)間是二維的,簡單的排序似乎是行不通的(通過lhs或者rhs來排序似乎都不太好)
莫隊為我們提供了一個排序方法,假設(shè)有Q個詢問,區(qū)間范圍是1~N,那么可以對左端點分塊,分成根號N塊,這樣就先把不同塊的左端點排好序了,接下來同一塊的詢問根據(jù)右端點排序,這樣就形成了一個(比較科學(xué))的序列,可以使左右端點移動的距離變小,變相減小了復(fù)雜度。
實際上莫隊就是一個科學(xué)的二維數(shù)組排序方法嘛
然后對于這道題因為是求概率,每次如果都維護一個分?jǐn)?shù)很困難,所以選擇每次維護分子和分母,這樣這個概率公式就很好求了,分母只與區(qū)間長度有關(guān),分子就是(每種襪子數(shù)目*(每種襪子數(shù)目-1))然后求和,化簡后發(fā)現(xiàn)只要求(每種襪子數(shù)目)的平方的和就可以了,最后求一下gcd把分?jǐn)?shù)表示成最簡形式
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <vector> 5 #include <map> 6 #include <math.h> 7 #include <string> 8 #include <algorithm> 9 #include <time.h> 10 11 #define SIGMA_SIZE 26 12 #define lson rt<<1 13 #define rson rt<<1|1 14 #define lowbit(x) (x&-x) 15 #define goe(i, a, b) for(int i=a; i<=b; i++) 16 #define go(i, a, b) for(int i = a; i < b; i++); 17 #pragma warning ( disable : 4996 ) 18 19 using namespace std; 20 typedef long long LL; 21 inline LL LMax(LL a,LL b) { return a>b?a:b; } 22 inline LL LMin(LL a,LL b) { return a>b?b:a; } 23 inline LL lgcd( LL a, LL b ) { return b==0?a:lgcd(b,a%b); } 24 inline LL llcm( LL a, LL b ) { return a/lgcd(a,b)*b; } //a*b = gcd*lcm 25 inline int Max(int a,int b) { return a>b?a:b; } 26 inline int Min(int a,int b) { return a>b?b:a; } 27 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); } 28 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm 29 const LL INF = 0x3f3f3f3f3f3f3f3f; 30 const LL mod = 1000000007; 31 const double eps = 1e-8; 32 const int inf = 0x3f3f3f3f; 33 const int maxk = 5e4+5; 34 const int maxn = 3e4+5; 35 36 int belong[maxk], wazi[maxk]; 37 int N, M, unit; 38 LL ans, cnt[maxk], fina[maxk], finb[maxk]; 39 struct query { 40 int lhs, rhs, id; 41 LL a, b; //分子和分母 42 }q[maxk]; 43 44 bool cmp(const query& a, const query& b) 45 { 46 if (belong[a.lhs] == belong[b.lhs]) return a.rhs < b.rhs; 47 return a.lhs < b.lhs; 48 } 49 50 void revise( int x, int d ) 51 { 52 //ans先減去以前的貢獻再加上現(xiàn)在的貢獻 53 ans -= cnt[wazi[x]]*cnt[wazi[x]]; 54 cnt[wazi[x]] += d; 55 ans += cnt[wazi[x]]*cnt[wazi[x]]; 56 } 57 58 void read() 59 { 60 scanf("%d %d", &N, &M); unit = sqrt(N); 61 62 63 goe(i, 1, N) { scanf("%d", &wazi[i]); belong[i] = i/unit+1; } 64 goe(i, 1, M) { scanf("%d %d", &q[i].lhs, &q[i].rhs); q[i].id = i; } 65 sort(q+1, q+1+M, cmp); 66 67 } 68 69 70 int main() 71 { 72 read(); 73 74 int l = 1, r = 0; 75 ans = 0; 76 77 goe(i, 1, M) 78 { 79 //如果是減操作,則先要執(zhí)行revise, 80 //注意順序 81 while(l < q[i].lhs) { revise(l, -1); l++; } 82 while(l > q[i].lhs) { l--; revise(l, 1); } 83 while(r < q[i].rhs) { r++; revise(r, 1); } 84 while(r > q[i].rhs) { revise(r, -1); r--; } 85 86 if ( q[i].lhs == q[i].rhs ) { q[i].a = 0; q[i].b = 1; continue; } 87 q[i].a = ans - (q[i].rhs - q[i].lhs + 1); 88 q[i].b = (LL)1*(q[i].rhs - q[i].lhs)*(q[i].rhs - q[i].lhs + 1); 89 90 LL Gcd = lgcd(q[i].a, q[i].b); 91 q[i].a /= Gcd; 92 q[i].b /= Gcd; 93 } 94 95 goe(i, 1, M) 96 { 97 fina[q[i].id] = q[i].a; 98 finb[q[i].id] = q[i].b; 99 } 100 101 goe(i, 1, M) 102 printf("%lld/%lld\n", fina[i], finb[i]); 103 104 return 0; 105 } View Code?
待修改的莫隊,這就有點難受了,原先的二維區(qū)間變成了三維,加了一個時間維度(怎么感覺怪怪的),大意就是原先每個結(jié)構(gòu)體多存儲一個了時間變量,修改操作也通過時間變量儲存,并且通過change這一數(shù)據(jù)結(jié)構(gòu)將一系列修改的數(shù)據(jù)存儲下來(即能通過時間確定修改的值,就像能操縱時間,隨時可以通過時間還原原來的序列或者變化成修改后的序列),然后就是在原始莫隊的基礎(chǔ)上加上一個時間維度的移動,不得不說這個算法真是樸素而美妙...
HYSBZ 2120 數(shù)顏色---模板題
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <vector> 5 #include <map> 6 #include <math.h> 7 #include <string> 8 #include <algorithm> 9 #include <time.h> 10 11 #define SIGMA_SIZE 26 12 #define lson rt<<1 13 #define rson rt<<1|1 14 #define lowbit(x) (x&-x) 15 #define foe(i, a, b) for(int i=a; i<=b; i++) 16 #define fo(i, a, b) for(int i = a; i < b; i++); 17 #pragma warning ( disable : 4996 ) 18 19 using namespace std; 20 typedef long long LL; 21 inline LL LMax(LL a,LL b) { return a>b?a:b; } 22 inline LL LMin(LL a,LL b) { return a>b?b:a; } 23 inline LL lgcd( LL a, LL b ) { return b==0?a:lgcd(b,a%b); } 24 inline LL llcm( LL a, LL b ) { return a/lgcd(a,b)*b; } //a*b = gcd*lcm 25 inline int Max(int a,int b) { return a>b?a:b; } 26 inline int Min(int a,int b) { return a>b?b:a; } 27 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); } 28 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm 29 const LL INF = 0x3f3f3f3f3f3f3f3f; 30 const LL mod = 1000000007; 31 const double eps = 1e-8; 32 const int inf = 0x3f3f3f3f; 33 const int maxk = 1e6+5; 34 const int maxn = 1e4+5; 35 36 37 int belong[maxn], num[maxn], now[maxn], fin[maxn]; 38 int N, Q, unit, l, r; 39 int t, T, Time, ans; 40 int cnt[maxk]; 41 42 struct query { 43 int lhs, rhs, tim, id; 44 query() {} 45 query(int l, int r, int t, int i) 46 : lhs(l), rhs(r), tim(t), id(i) {} 47 48 }q[maxn]; 49 50 // pos表示修改的位置,new表示修改后的值,Old表示修改前的值 51 struct change { 52 int pos, New, Old; 53 change() {} 54 change(int p, int n, int o) 55 : pos(p), New(n), Old(o) {} 56 }c[maxn]; 57 58 bool cmp(const query& a, const query& b) 59 { 60 if (belong[a.lhs != b.lhs]) return a.lhs < b.lhs; 61 if (belong[a.rhs != b.rhs]) return a.rhs < b.rhs; 62 return a.tim < b.tim; 63 } 64 65 void read() 66 { 67 //最優(yōu)分塊策略不再是根號 68 scanf("%d %d", &N, &Q); unit = pow((double)N, (double)0.666666); 69 foe(i, 1, N) { scanf("%d", &num[i]); now[i] = num[i]; belong[i] = i/unit+1; } 70 71 char str[2]; 72 int x, y; t = T = 0; 73 foe(i, 1, Q) 74 { 75 scanf("%s %d %d", str, &x, &y); 76 if (str[0] == 'Q') q[++t] = query(x, y, T, t); 77 if (str[0] == 'R') { c[++T] = change(x, y, now[x]); now[x] = y; } 78 } 79 sort(q+1, q+t+1, cmp); 80 } 81 82 void revise(int x, int d) 83 { 84 cnt[x] += d; 85 if (d > 0) 86 ans += (cnt[x] == 1); //只有從0加到1才會增加一種顏色數(shù)量 87 if (d < 0) 88 ans -= (cnt[x] == 0); 89 } 90 91 void reviseT(int x, int d) 92 { 93 if ( x >= l && x <= r ) 94 { 95 revise(d, 1); 96 revise(num[x], -1); 97 } 98 num[x] = d; 99 } 100 101 int main() 102 { 103 read(); 104 105 l = 1; r = 0; Time = 0; 106 foe(i, 1, t) 107 { 108 //若修改時間小于tim,則要添加新的修改 109 while(Time < q[i].tim) { Time++; reviseT(c[Time].pos, c[Time].New); } 110 //若time大于tim,則還原老修改 111 while(Time > q[i].tim) { reviseT(c[Time].pos, c[Time].Old); Time--; } 112 113 while(l < q[i].lhs) { revise(num[l], -1); l++; } 114 while(l > q[i].lhs) { l--; revise(num[l], 1); } 115 while(r < q[i].rhs) { r++; revise(num[r], 1); } 116 while(r > q[i].rhs) { revise(num[r], -1); r--; } 117 118 fin[q[i].id] = ans; 119 120 } 121 122 foe(i, 1, t) 123 printf("%d\n", fin[i]); 124 125 return 0; 126 } View Code?
轉(zhuǎn)載于:https://www.cnblogs.com/chaoswr/p/9341987.html
總結(jié)
以上是生活随笔為你收集整理的初识莫队——小Z的袜子的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Luogu P1091 合唱队形
- 下一篇: 魔戒(广搜)