首页 > 代码库 > 莫队算法学习笔记【BZOJ2038:小Z的袜子】【SPOJ3267:D-query】
莫队算法学习笔记【BZOJ2038:小Z的袜子】【SPOJ3267:D-query】
很久以前傻乎乎地看来源奇怪的资料的时候被各种曼哈顿弄晕了。
然后现在学会的是分块方法。另新创一个分块方法。
让我们考虑这样一个区间询问问题……
它有如下的性质:
0,n个数,Q个询问。
1,它没有修改操作,这意味着我们可以按我们喜欢的次序跟询问玩耍。实际上后面会讲到我们完全可以按任意次序玩耍。
2,如果我们知道区间询问 [L , R] 对应的值,我们可以轻易求出 [L±1 , R] 和 [L , R±1] 的值。
(其实如果限制增加,比如只能求 [L+1 , R] 和 [L , R-1] 的值,同样不影响问题的解决,——这是我口胡的,没写过,日后再说。我不知道在传统莫队中是怎样的,在新算法中可行。)
3,若2中操作对应复杂度为P,而你需要用O(P*n^1.5)(明明是)O(P*n*Q^0.5)的复杂度来解决问题,那么莫队算法就是你需要的了。
离线方法:将n个数分成sqrt(n)块(一堆这么说的人什么心态)sqrt(Q)块
询问按区间排序,以L所在块序号为第一关键字,R为第二关键字,进行排序。(传统莫队就是如此搞法,YY一下就好了)
不过不知道这个做法怎么流传开的,感觉毫无优点吗……尤其是复杂度这么误导真的好吗。
大部分此类区间题Q总是大于N的(大很多!……)。然后正确的曼哈顿树求法最坏是n*sqrt(m),分块也可以做到这个复杂度,但无脑分块直接成了m*sqrt(n)……
总有一天要出摸你赛把你们卡光光……
(
但是话说回来……随机数据里sqrt(n)块好像还要快一点……
随机数据里明显是把块弄得越大越好吗……
)
先说【SPOJ3267:D-query】,毕竟是要做这道才会的。
不过能想用莫队A这题的也不多。
题目描述
输入
第一行:一个整数n,表示数列的长度。
第二行:n个整数a1,a2,a3,···,an(1 <= ai <= 10^6),数与数之间用一个空格隔开。
第三行:一个整数q(<= 200000),表示询问的次数。
接下来q行,每行两个整数i,j(1 <= i <= j <= n <= 30000),表示要询问的区间。
输出
对于每一询问,输出在这个区间内不同的数字个数。
样例输入
样例输出
无特别。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using
namespace
std;
#define rep(i,j,n) for(int i=j;i<=n;i++)
char
c;
template
<
class
T>
inline
void
read(T&x){
for
(c=
getchar
();c<
‘0‘
||c>
‘9‘
;c=
getchar
());
for
(x=0;c>=
‘0‘
&&c<=
‘9‘
;c=
getchar
())x=x*10+c-
‘0‘
;};
struct
E{
int
x,y,l,list;}d[200010];
int
next[30010],prev[30010],p[1000010],ans[200010];
int
n,nn,x,T;
bool
cmp(E x,E y){
return
x.l<y.l||(x.l==y.l&&x.y<y.y);}
int
main(){
read(n);
memset
(next,63,n<<2);
rep(i,1,n){
read(x);
if
(!p[x])p[x]=i;
else
prev[i]=p[x],next[p[x]]=i,p[x]=i;
}
read(T);nn=
int
(
sqrt
(n));//块长……理论上的话……改成n/sqrt
(m)比较好
rep(i,1,T){read(d[i].x);read(d[i].y);d[i].l=(d[i].x+nn-1)/nn;d[i].list=i;}
sort(d+1,d+T+1,cmp);
int
ll,lr,now;
rep(ii,1,T){
if
(d[ii].l!=d[ii-1].l){
now=0;
rep(i,d[ii].x,d[ii].y)
if
(prev[i]<d[ii].x) now++;
ans[d[ii].list]=now;
}
else
{
if
(d[ii-1].x<d[ii].x){
ll=d[ii-1].x;lr=d[ii].x-1;
rep(i,ll,lr)
if
(next[i]>d[ii-1].y) now--;
}
else
{
ll=d[ii].x;lr=d[ii-1].x-1;
rep(i,ll,lr)
if
(next[i]>d[ii-1].y) now++;
}
ll=d[ii-1].y+1;lr=d[ii].y;
rep(i,ll,lr)
if
(prev[i]<d[ii].x) now++;
ans[d[ii].list]=now;
}
}
rep(i,1,T)
printf
(
"%d\n"
,ans[i]);
}
莫队算法学习笔记【BZOJ2038:小Z的袜子】【SPOJ3267:D-query】