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

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

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

线段树可以支持修改,并且单次操作时间复杂度一般为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

 /**************************************************************
Problem: 2038
User: idy002
Language: C++
Result: Accepted
Time:836 ms
Memory:2972 kb
****************************************************************/ #include <cstdio>
#include <cmath>
#include <algorithm>
#define maxn 50010
using namespace std; typedef long long lng; lng gcd( lng a, lng b ) {
return b ? gcd(b,a%b) : a;
}
struct Query {
int l, r, idx;
lng ans[];
Query(){}
Query( int l, int r, int idx ) : l(l), r(r), idx(idx) {}
void set( lng sum ) {
lng len = (r-l+);
lng a = sum, b = len*(len-)/;
lng c = gcd(a,b);
if( c ) {
ans[] = a/c;
ans[] = b/c;
} else {
ans[] = ;
ans[] = ;
}
}
};
bool cmpl( const Query & a, const Query & b ) {
return a.l < b.l;
}
bool cmpr( const Query & a, const Query & b ) {
return a.r < b.r;
}
bool cmpid( const Query & a, const Query & b ) {
return a.idx < b.idx;
}
struct Range {
int l, r;
Range(){}
Range( int l, int r ) : l(l), r(r) {}
}; int n, m;
int clr[maxn], cnt[maxn];
int tot;
Range rng[maxn];
Query qry[maxn]; void partition() {
int len = (int)ceil(sqrt(n));
tot = n/len;
for( int i=; i<=tot; i++ ) {
rng[i].l = rng[i-].r+;
rng[i].r = rng[i-].r+len;
}
if( rng[tot].r < n ) {
tot++;
rng[tot].l = rng[tot-].r+;
rng[tot].r = n;
}
} void work() {
sort( qry+, qry++m, cmpl );
int s, t;
lng sum;
s = t = ;
for( int i=; i<=tot; i++ ) {
while( s<=m && qry[s].l<rng[i].l ) s++;
while( t<=m && qry[t].l<=rng[i].r ) t++;
if( s>m || qry[s].l>rng[i].r ) continue;
sort( qry+s, qry+t, cmpr );
sum = ;
for( int j=qry[s].l; j<=qry[s].r; j++ )
sum += cnt[clr[j]]++;
qry[s].set( sum );
for( int q=s+; q<t; q++ ) {
if( qry[q].l > qry[q-].r ) { // ( ) [ ]
for( int j=qry[q-].l; j<=qry[q-].r; j++ )
cnt[clr[j]]--;
sum = ;
for( int j=qry[q].l; j<=qry[q].r; j++ )
sum += cnt[clr[j]]++;
} else if( qry[q].l <= qry[q-].l ) { // [ ( ) ]
for( int j=qry[q].l; j<qry[q-].l; j++ )
sum += cnt[clr[j]]++;
for( int j=qry[q-].r+; j<=qry[q].r; j++ )
sum += cnt[clr[j]]++;
} else { // ( [ ) ]
for( int j=qry[q-].l; j<qry[q].l; j++ )
sum -= --cnt[clr[j]];
for( int j=qry[q-].r+; j<=qry[q].r; j++ )
sum += cnt[clr[j]]++;
}
qry[q].set( sum );
}
for( int j=qry[t-].l; j<=qry[t-].r; j++ )
cnt[clr[j]]--;
}
} int main() {
scanf( "%d%d", &n, &m );
for( int i=; i<=n; i++ ) scanf( "%d", clr+i );
for( int i=,l,r; i<=m; i++ ) {
scanf( "%d%d", &l, &r );
qry[i] = Query( l, r, i );
}
partition();
work();
sort( qry+, qry++m, cmpid );
for( int i=; i<=m; i++ )
printf( "%lld/%lld\n", qry[i].ans[], qry[i].ans[] );
}

nbut 1457:

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

 #include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define maxn 100010
using namespace std; typedef long long lng; struct Qu {
int l, r, id;
};
bool cmpl( const Qu & a, const Qu & b ) {
return a.l < b.l;
}
bool cmpr( const Qu & a, const Qu & b ) {
return a.r < b.r;
} int n, m;
int idx[maxn], itot;
int clr[maxn];
lng cnt[maxn];
int lx[maxn], rx[maxn], stot;
Qu qu[maxn];
lng ans[maxn]; void partition() {
int len = (int)ceil(sqrt(n));
stot = n/len;
rx[] = ;
for( int i=; i<=stot; i++ ) {
lx[i] = rx[i-]+;
rx[i] = rx[i-]+len;
}
if( rx[stot]!=n ) {
stot++;
lx[stot] = rx[stot-]+;
rx[stot] = n;
}
}
void makeid() {
sort( idx+, idx++n );
int tot = unique( idx+, idx++n ) - idx;
for( int i=; i<=n; i++ )
clr[i] = lower_bound( idx+, idx+tot, clr[i] ) - idx;
}
lng cube( lng a ) {
return a*a*a;
}
void update( lng &sum, int c, int delta ) {
sum -= cube(cnt[c]);
cnt[c] += delta;
sum += cube(cnt[c]);
}
void work() {
sort( qu+, qu++m, cmpl );
for( int i=,s=,t=; i<=stot; i++ ) {
memset( cnt, , sizeof(cnt) );
while( s<=m && qu[s].l<lx[i] ) s++;
while( t<=m && qu[t].l<=rx[i] ) t++;
sort( qu+s, qu+t, cmpr ); lng sum = ;
for( int j=qu[s].l; j<=qu[s].r; j++ )
update( sum, clr[j], + );
ans[qu[s].id] = sum;
for( int q=s+; q<t; q++ ) {
if( qu[q].l<=qu[q-].l ) {
// [ ( ) ]
for( int j=qu[q].l; j<qu[q-].l; j++ )
update( sum, clr[j], + );
for( int j=qu[q-].r+; j<=qu[q].r; j++ )
update( sum, clr[j], + );
} else if( qu[q].l>qu[q-].r ) {
// ( ) [ ]
for( int j=qu[q-].l; j<=qu[q-].r; j++ )
cnt[clr[j]]--;
sum = ;
for( int j=qu[q].l; j<=qu[q].r; j++ )
update( sum, clr[j], + );
} else {
// ( [ ) ]
for( int j=qu[q-].l; j<qu[q].l; j++ )
update( sum, clr[j], - );
for( int j=qu[q-].r+; j<=qu[q].r; j++ )
update( sum, clr[j], + );
}
ans[qu[q].id] = sum;
}
}
}
int main() {
scanf( "%d", &n );
for( int i=; i<=n; i++ ) {
scanf( "%d", idx+i );
clr[i] = idx[i];
}
scanf( "%d", &m );
for( int i=; i<=m; i++ ) {
scanf( "%d%d", &qu[i].l, &qu[i].r );
qu[i].id = i;
}
makeid();
partition();
work();
for( int i=; i<=m; i++ )
printf( "%lld\n", ans[i] );
}

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

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

    题意: 给一段序列,询问一个区间,求出区间中.....woc! 贴原题! 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过 ...

  2. bzoj 2038 小z的袜子 莫队

    莫队大法好,入坑保平安 只要能O(1)或O(log)转移,离线莫队貌似真的无敌. #include<cstdio> #include<iostream> #include< ...

  3. bzoj 2038 小Z的袜子 莫队算法

    题意 给你一个长度序列,有多组询问,每次询问(l,r)任选两个数相同的概率.n <= 50000,数小于等于n. 莫队算法裸题. 莫队算法:将序列分为根号n段,将询问排序,以L所在的块为第一关键 ...

  4. bzoj 2308 小Z的袜子(莫队算法)

    小Z的袜子 [题目链接]小Z的袜子 [题目类型]莫队算法 &题解: 莫队算法第一题吧,建议先看这个理解算法,之后在参考这个就可以写出简洁的代码 我的比第2个少了一次sort,他的跑了1600m ...

  5. 小Z的袜子 & 莫队

    莫队学习 & 小Z的袜子 引入 莫队 由莫涛巨佬提出,是一种离线算法 运用广泛 可以解决广大的离线区间询问题 莫队的历史 早在mt巨佬提出莫队之前 类似莫队的算法和莫队的思想已在Codefor ...

  6. BZOJ 2038 [2009国家集训队]小Z的袜子 莫队

    2038: [2009国家集训队]小Z的袜子(hose) 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=2038 Descriptionw ...

  7. (原创)BZOJ 2038 小Z的袜子(hose) 莫队入门题+分块

    I - 小Z的袜子(hose) 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z ...

  8. BZOJ - 2038 小Z的袜子(普通莫队)

    题目链接:小Z的袜子 题意:$n$只袜子,$m$个询问,每次回答有多大概率在$[L,R]$区间内抽到两只颜色相同的袜子 思路:普通莫队,如果两个询问左端点在一个块内,则按询问右端点排序,否则按照所在块 ...

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

    2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 11542  Solved: 5166[Sub ...

随机推荐

  1. Django之kindeditor

    1.什么是kindeditor? KindEditor是一套开源的HTML可视化编辑器,主要用于让用户在网站上获得所见即所得编辑效果,兼容IE.Firefox.Chrome.Safari.Opera等 ...

  2. 环境变量配错了 command not found

    一般就是忘记在PATH 前面加$ 1.可以用whereis或者which命令查看一下有没有这个命令 具体执行which lswhereis ls 2.系统环境变量导致的问题解决方案: exportPA ...

  3. 夜神安卓模拟器adb命令详解

    https://www.yeshen.com/faqs/H15tDZ6YW 一.如何找到adb? 安装夜神安卓模拟器后,电脑桌面会有"夜神模拟器"的启动图标,鼠标右键--打开文件所 ...

  4. openjudge-NOI 2.6-1944 吃糖果

    题目链接:http://noi.openjudge.cn/ch0206/1944/ 题解: 递推,题目中给出了很详细的过程,不讲解 #include<cstdio> int n; int ...

  5. Class.this 和 this 的有什么不同

    转载:http://www.cnblogs.com/liwei45212/archive/2013/04/17/3026364.html 在阅读Java代码的時候 我们时会看到Class.this的使 ...

  6. C++——sort和stable_sort的若干区别

    版权声明:本文系作者原创,转载请注明出处. C++中sort和stable_sort的区别: sort是快速排序实现,因此是不稳定的:stable_sort是归并排序实现,因此是稳定的: 对于相等的元 ...

  7. supervisor 的使用

    1.通过yum安装 supervisor: 2.supervisorctl 查看状态: 3.supervisor.d 下查看配置文件,修改命令和日志目录 4.tail -f /var/log/supe ...

  8. linux中getmntent setmntent endmntent 用法例子

    mntent 结构是在 <mntent.h> 中定义,如下:               struct mntent {                      char    *mnt ...

  9. 对cgic的理解——name选项

    #include <stdio.h>#include <stdlib.h>#include <string.h>#include "cgic.h" ...

  10. JMeter -----设置代理抓取web的HTTPS请求,“您的连接不是私密链接”的处理方案

    出现如上截图的问题,已确定将网站的证书.jmeter的证书均安装完成,并未提示报错,但是在配置代理后,刷新网站抓取请求时总是提示如上报错 解决方案: 1.关闭电脑上的所有浏览器 2.打开“终端”运行: ...