bzoj 2038 小z的袜子 莫队例题

莫队,利用可以快速地通过一个问题的答案得到另一问题的答案这一特性,合理地组织问题的求解顺序,将已解决的问题帮助解决当前问题,来优化时间复杂度。

典型用法:处理静态(无修改)离线区间查询问题。

线段树也是处理区间问题的一个有力工具,它和莫队算法各有特点:

线段树可以支持修改,并且单次操作时间复杂度一般为O(log),支持在线,但是要求可以进行快速的区间合并操作,两个区间如不能快速合并(f(n)*O(log)>O(n)),则用线段树就没有什么实际价值了(暴力都比它块)

莫队算法可以解决某些线段树不能解决的静态离线问题,但它要求可以快速地从一个答案得到另一个答案。

对于区间问题,假如我们得到了区间[l,r]的答案,能通过它用O(1)的时间得到[l-1,r],[l+1,r],[l,r-1],[l,r+1]的答案,那么我们将[l,r]看成二维平面上的点,两个点的距离用哈密顿距离表示,一个不错的想法是找到图的最小生成树,然后暴力推出一个点,其它点从它延伸过去就行了,时间复杂度是距离和加上暴力的那个点花的时间。

这道题除了上面的做法,还可以分块,如果分成n^0.5块,可以做到O(n^1.5)的复杂度。

做法是先将原颜色序列分成根号n块,然后将询问先按左端点排序,对于每一块的询问再按右端点排序(都是升序)。

每次计算一个左端点在一个块中的询问,先暴力这个区间的第一个询问,然后后面的每个询问从它前一个询问推(具体看代码)

时间复杂度可以这么看:

排序O(nlogn)

对于每个询问,它从前一个推过来,因为它们在同一块中,前端点改变最多O(n^0.5)次,有O(n)个询问,所以前端点变化O(n*n^0.5)次

对于每一段,后端点变化是O(n)的,而最多有O(n^0.5)段,所以后端点变化O(n^0.5*n)次

所以从一个[l,r]推到它四个相邻的点的次数是O(n^1.5),而转移是O(1)的,所以总的复杂度是O(n^1.5)。

不论是分块还是最小生成树,都是想用最少的转移和最少的暴力将所有询问解决。

不会写最小生成树的做法。这个是分块,感谢proverbs

  1 /**************************************************************
  2     Problem: 2038
  3     User: idy002
  4     Language: C++
  5     Result: Accepted
  6     Time:836 ms
  7     Memory:2972 kb
  8 ****************************************************************/
  9
 10 #include <cstdio>
 11 #include <cmath>
 12 #include <algorithm>
 13 #define maxn 50010
 14 using namespace std;
 15
 16 typedef long long lng;
 17
 18 lng gcd( lng a, lng b ) {
 19     return b ? gcd(b,a%b) : a;
 20 }
 21 struct Query {
 22     int l, r, idx;
 23     lng ans[2];
 24     Query(){}
 25     Query( int l, int r, int idx ) : l(l), r(r), idx(idx) {}
 26     void set( lng sum ) {
 27         lng len = (r-l+1);
 28         lng a = sum, b = len*(len-1)/2;
 29         lng c = gcd(a,b);
 30         if( c ) {
 31             ans[0] = a/c;
 32             ans[1] = b/c;
 33         } else {
 34             ans[0] = 0;
 35             ans[1] = 1;
 36         }
 37     }
 38 };
 39 bool cmpl( const Query & a, const Query & b ) {
 40     return a.l < b.l;
 41 }
 42 bool cmpr( const Query & a, const Query & b ) {
 43     return a.r < b.r;
 44 }
 45 bool cmpid( const Query & a, const Query & b ) {
 46     return a.idx < b.idx;
 47 }
 48 struct Range {
 49     int l, r;
 50     Range(){}
 51     Range( int l, int r ) : l(l), r(r) {}
 52 };
 53
 54 int n, m;
 55 int clr[maxn], cnt[maxn];
 56 int tot;
 57 Range rng[maxn];
 58 Query qry[maxn];
 59
 60 void partition() {
 61     int len = (int)ceil(sqrt(n));
 62     tot = n/len;
 63     for( int i=1; i<=tot; i++ ) {
 64         rng[i].l = rng[i-1].r+1;
 65         rng[i].r = rng[i-1].r+len;
 66     }
 67     if( rng[tot].r < n ) {
 68         tot++;
 69         rng[tot].l = rng[tot-1].r+1;
 70         rng[tot].r = n;
 71     }
 72 }
 73
 74 void work() {
 75     sort( qry+1, qry+1+m, cmpl );
 76     int s, t;
 77     lng sum;
 78     s = t = 1;
 79     for( int i=1; i<=tot; i++ ) {
 80         while( s<=m && qry[s].l<rng[i].l ) s++;
 81         while( t<=m && qry[t].l<=rng[i].r ) t++;
 82         if( s>m || qry[s].l>rng[i].r ) continue;
 83         sort( qry+s, qry+t, cmpr );
 84         sum = 0;
 85         for( int j=qry[s].l; j<=qry[s].r; j++ )
 86             sum += cnt[clr[j]]++;
 87         qry[s].set( sum );
 88         for( int q=s+1; q<t; q++ ) {
 89             if( qry[q].l > qry[q-1].r ) {    //  ( ) [ ]
 90                 for( int j=qry[q-1].l; j<=qry[q-1].r; j++ )
 91                     cnt[clr[j]]--;
 92                 sum = 0;
 93                 for( int j=qry[q].l; j<=qry[q].r; j++ )
 94                     sum += cnt[clr[j]]++;
 95             } else if( qry[q].l <= qry[q-1].l ) {    //  [ ( ) ]
 96                 for( int j=qry[q].l; j<qry[q-1].l; j++ )
 97                     sum += cnt[clr[j]]++;
 98                 for( int j=qry[q-1].r+1; j<=qry[q].r; j++ )
 99                     sum += cnt[clr[j]]++;
100             } else {    //  ( [ ) ]
101                 for( int j=qry[q-1].l; j<qry[q].l; j++ )
102                     sum -= --cnt[clr[j]];
103                 for( int j=qry[q-1].r+1; j<=qry[q].r; j++ )
104                     sum += cnt[clr[j]]++;
105             }
106             qry[q].set( sum );
107         }
108         for( int j=qry[t-1].l; j<=qry[t-1].r; j++ )
109             cnt[clr[j]]--;
110     }
111 }
112
113 int main() {
114     scanf( "%d%d", &n, &m );
115     for( int i=1; i<=n; i++ ) scanf( "%d", clr+i );
116     for( int i=1,l,r; i<=m; i++ ) {
117         scanf( "%d%d", &l, &r );
118         qry[i] = Query( l, r, i );
119     }
120     partition();
121     work();
122     sort( qry+1, qry+1+m, cmpid );
123     for( int i=1; i<=m; i++ )
124         printf( "%lld/%lld\n", qry[i].ans[0], qry[i].ans[1] );
125 }

nbut 1457:

询问一个区间中每种颜色的数量的立方和

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cmath>
  4 #include <algorithm>
  5 #define maxn 100010
  6 using namespace std;
  7
  8 typedef long long lng;
  9
 10 struct Qu {
 11     int l, r, id;
 12 };
 13 bool cmpl( const Qu & a, const Qu & b ) {
 14     return a.l < b.l;
 15 }
 16 bool cmpr( const Qu & a, const Qu & b ) {
 17     return a.r < b.r;
 18 }
 19
 20
 21 int n, m;
 22 int idx[maxn], itot;
 23 int clr[maxn];
 24 lng cnt[maxn];
 25 int lx[maxn], rx[maxn], stot;
 26 Qu qu[maxn];
 27 lng ans[maxn];
 28
 29
 30 void partition() {
 31     int len = (int)ceil(sqrt(n));
 32     stot = n/len;
 33     rx[0] = 0;
 34     for( int i=1; i<=stot; i++ ) {
 35         lx[i] = rx[i-1]+1;
 36         rx[i] = rx[i-1]+len;
 37     }
 38     if( rx[stot]!=n ) {
 39         stot++;
 40         lx[stot] = rx[stot-1]+1;
 41         rx[stot] = n;
 42     }
 43 }
 44 void makeid() {
 45     sort( idx+1, idx+1+n );
 46     int tot = unique( idx+1, idx+1+n ) - idx;
 47     for( int i=1; i<=n; i++ )
 48         clr[i] = lower_bound( idx+1, idx+tot, clr[i] ) - idx;
 49 }
 50 lng cube( lng a ) {
 51     return a*a*a;
 52 }
 53 void update( lng &sum, int c, int delta ) {
 54     sum -= cube(cnt[c]);
 55     cnt[c] += delta;
 56     sum += cube(cnt[c]);
 57 }
 58 void work() {
 59     sort( qu+1, qu+1+m, cmpl );
 60     for( int i=1,s=1,t=1; i<=stot; i++ ) {
 61         memset( cnt, 0, sizeof(cnt) );
 62         while( s<=m && qu[s].l<lx[i] ) s++;
 63         while( t<=m && qu[t].l<=rx[i] ) t++;
 64         sort( qu+s, qu+t, cmpr );
 65
 66         lng sum = 0;
 67         for( int j=qu[s].l; j<=qu[s].r; j++ )
 68             update( sum, clr[j], +1 );
 69         ans[qu[s].id] = sum;
 70         for( int q=s+1; q<t; q++ ) {
 71             if( qu[q].l<=qu[q-1].l ) {
 72                 //    [ ( ) ]
 73                 for( int j=qu[q].l; j<qu[q-1].l; j++ )
 74                     update( sum, clr[j], +1 );
 75                 for( int j=qu[q-1].r+1; j<=qu[q].r; j++ )
 76                     update( sum, clr[j], +1 );
 77             } else if( qu[q].l>qu[q-1].r ) {
 78                 //    ( ) [ ]
 79                 for( int j=qu[q-1].l; j<=qu[q-1].r; j++ )
 80                     cnt[clr[j]]--;
 81                 sum = 0;
 82                 for( int j=qu[q].l; j<=qu[q].r; j++ )
 83                     update( sum, clr[j], +1 );
 84             } else {
 85                 //    ( [ ) ]
 86                 for( int j=qu[q-1].l; j<qu[q].l; j++ )
 87                     update( sum, clr[j], -1 );
 88                 for( int j=qu[q-1].r+1; j<=qu[q].r; j++ )
 89                     update( sum, clr[j], +1 );
 90             }
 91             ans[qu[q].id] = sum;
 92         }
 93     }
 94 }
 95 int main() {
 96     scanf( "%d", &n );
 97     for( int i=1; i<=n; i++ ) {
 98         scanf( "%d", idx+i );
 99         clr[i] = idx[i];
100     }
101     scanf( "%d", &m );
102     for( int i=1; i<=m; i++ ) {
103         scanf( "%d%d", &qu[i].l, &qu[i].r );
104         qu[i].id = i;
105     }
106     makeid();
107     partition();
108     work();
109     for( int i=1; i<=m; i++ )
110         printf( "%lld\n", ans[i] );
111 }

时间: 2024-10-10 03:01:06

bzoj 2038 小z的袜子 莫队例题的相关文章

BZOJ 2038 小z的袜子 &amp; 莫队算法(不就是个暴力么..)

题意: 给一段序列,询问一个区间,求出区间中.....woc! 贴原题! 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬. 你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子.当然,小Z希望这个概

BZOJ 2038 小Z的袜子(莫队算法)

莫队算法如果我们已知[l,r]的答案,能在O(1)时间得到[l+1,r]的答案以及[l,r-1]的答案,即可使用莫队算法.时间复杂度为O(n^1.5).如果只能在logn的时间移动区间,则时间复杂度是O(n^1.5*log n).其实就是找一个数据结构支持插入.删除时维护当前答案. 这道题的话我们很容易用数组来实现,做到O(1)的从[l,r]转移到[l,r+1]与[l+1,r]. 那么莫队算法怎么做呢?以下都是在转移为O(1)的基础下讨论的时间复杂度.另外由于n与m同阶,就统一写n.如果已知[l

bzoj 2038 小Z的袜子(hose)(莫队算法)

2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 11542  Solved: 5166[Submit][Status][Discuss] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命--具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两

清橙A1206 小Z的袜子(莫队算法)

A1206. 小Z的袜子 时间限制:1.0s   内存限制:512.0MB 总提交次数:744   AC次数:210   平均分:44.44 将本题分享到: 查看未格式化的试题   提交   试题讨论 试题来源 2010中国国家集训队命题答辩 问题描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是

BZOJ 2038 小Z的袜子(hose) (莫队离线)

题目地址:BZOJ 2038 裸的莫队算法. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <set> #include <stdio.h> #includ

BZOJ 2038 2009国家集训队 小Z的袜子 莫队算法

题目大意:给出一些袜子的排列顺序,每次问一段区间中有多少相同颜色的袜子对. 思路:莫队算法真是一个神奇的算法.首先,暴力枚举是O(n^2)的时间复杂度,这肯定是不行的.假如区间是保证不重合的,那么就可以将总的时间转移的复杂度降到O(n).很遗憾,题目中没有这个保证.于是乎,神秘的莫队就发明了一种神奇的算法. 对于每一个询问,我们将它看成一个平面上的点(x1,y1),同样的也就会有其他的点分布在平面中.假如还有一个点(x2,y2),那么我们从第一个区间转移到第二个区间需要改变的元素总数为|x1 -

P1494 [国家集训队]小Z的袜子 莫队模板

链接:https://www.luogu.org/problemnew/show/P1494 题目描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬. 你的任务便是告诉小Z,他有多大的概率抽到两只颜色相

Luogu 1494 - 小Z的袜子 - [莫队算法模板题][分块]

题目链接:https://www.luogu.org/problemnew/show/P1494 题目描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬. 你的任务便是告诉小Z,他有多大的概率抽到两只颜

Luogu 1494 - 小Z的袜子 - [莫队算法模板题]

题目链接:https://www.luogu.org/problemnew/show/P1494 题目描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬. 你的任务便是告诉小Z,他有多大的概率抽到两只颜