博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
初识莫队——小Z的袜子
阅读量:5276 次
发布时间:2019-06-14

本文共 6761 字,大约阅读时间需要 22 分钟。

以前一直觉得莫队是多么高大上的一种算法,然而仔细看了下发现其实并不复杂,实质上就是技巧性的暴力美学。

在我看来莫队是一种分块排序后降低复杂度的算法,当答案可以通过左右端点一个一个移动维护出来的时候就可以使用莫队了。

比如给你4个区间

(1, 2)

(99, 100)

(3, 4)

(101, 102)

如果你是傻傻的按照给定的顺序去移动左右端点,那就是先计算出(1,2)区间的值,然后左端点先从1移动到99,右端点从2移动到100,计算完(99,100)区间内的值,又哼哧哼哧的左端点从99移回3,右端点从100移回4,计算完(3,4)后又移动到(101,102),显然这样实在是太蠢了。

正常人都应该想到,那我把计算顺序改成(1,2)(3,4)(99,100)(101,102)不就行了,但是因为区间是二维的,简单的排序似乎是行不通的(通过lhs或者rhs来排序似乎都不太好)

莫队为我们提供了一个排序方法,假设有Q个询问,区间范围是1~N,那么可以对左端点分块,分成根号N块,这样就先把不同块的左端点排好序了,接下来同一块的询问根据右端点排序,这样就形成了一个(比较科学)的序列,可以使左右端点移动的距离变小,变相减小了复杂度。

实际上莫队就是一个科学的二维数组排序方法嘛

然后对于这道题因为是求概率,每次如果都维护一个分数很困难,所以选择每次维护分子和分母,这样这个概率公式就很好求了,分母只与区间长度有关,分子就是(每种袜子数目*(每种袜子数目-1))然后求和,化简后发现只要求(每种袜子数目)的平方的和就可以了,最后求一下gcd把分数表示成最简形式

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
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先减去以前的贡献再加上现在的贡献 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 //如果是减操作,则先要执行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

 

待修改的莫队,这就有点难受了,原先的二维区间变成了三维,加了一个时间维度(怎么感觉怪怪的),大意就是原先每个结构体多存储一个了时间变量,修改操作也通过时间变量储存,并且通过change这一数据结构将一系列修改的数据存储下来(即能通过时间确定修改的值,就像能操纵时间,随时可以通过时间还原原来的序列或者变化成修改后的序列),然后就是在原始莫队的基础上加上一个时间维度的移动,不得不说这个算法真是朴素而美妙...

HYSBZ 2120 数颜色---模板题

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
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 //最优分块策略不再是根号 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才会增加一种颜色数量 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

 

转载于:https://www.cnblogs.com/chaoswr/p/9341987.html

你可能感兴趣的文章
Windows 2003全面优化
查看>>
格而知之2:UIView的autoresizingMask属性探究
查看>>
我的Hook学习笔记
查看>>
寄Android开发Gradle你需要知道的知识
查看>>
整理推荐的CSS属性书写顺序
查看>>
css & input type & search icon
查看>>
C# 强制关闭当前程序进程(完全Kill掉不留痕迹)
查看>>
ssm框架之将数据库的数据导入导出为excel文件
查看>>
语音识别中的MFCC的提取原理和MATLAB实现
查看>>
0320-学习进度条
查看>>
MetaWeblog API Test
查看>>
移动、尺寸改变
查看>>
c# 文件笔记
查看>>
类和结构
查看>>
typeset shell 用法
查看>>
python 之 循环语句
查看>>
心得25--JDK新特性9-泛型1-加深介绍
查看>>
[转]ceph网络通信模块_以monitor模块为例
查看>>
HDOJ 1754 I Hate It(线段树基本操作)
查看>>
latex tree
查看>>