@IOU
2018-02-08T06:20:36.000000Z
字数 1505
阅读 913
莫队
信息
洛谷
看到题解里提到一种神奇的算法——莫队,据说可以解决一切区间问题。。。
核心思想就是利用当前求的区间的值推知接下来要求的区间的值,因为已知区间f[l][r],我们可以很快地求得区间或者,因而可以逐步地推出下一个区间要求的值。
但是如果单纯只是这样算的话,复杂度有,这也没有体现莫队”优雅的暴力“的美称。
于是就有了莫队算法的精华——分块,我们知道,在推出下一个区间的过程中,尽量地使下一个区间和当前的区间的交集最大,就可以让推的过程尽量地短,于是按照一定地顺序求解,也就是我们通常讲的离线算法,在此就显得尤为重要了。
单纯的排序,即以左端点为第一关键字,右端点为第二关键字,固然可以减少左指针的移动,但右边的指针则会跑来跑去。为了尽量让右指针来回的次数少,可以相对地牺牲一点左边。把所有的区间按左端点分块,相同一块的区间再按右端点排序,这样一来,单纯就一个块而言,左指针只会在自己那一块里移动,右指针只会从头到尾扫一遍,这样就节省了很多时间。
分块大多分为块,每块个区间,至于为什么这样分,我也不知道。。。
算一算时间复杂度,就是
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int judge[10000001];
int seq[50001],p[200001];
struct node
{
int l,r,t,id,ans;
}a[200001];
bool my_comp(const node x,const node y)
{
if(x.t==y.t)return x.r<y.r;
return x.l<y.l;
}
int main()
{
int n,m;
cin>>n;
int len=sqrt(n);
for(int i=1;i<=n;i++)
scanf("%d",&seq[i]);
cin>>m;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a[i].l,&a[i].r);
a[i].t=a[i].l/len;
a[i].id=i;
}
sort(a+1,a+1+m,my_comp);
int left=0,right=0,ans=0;
for(int i=1;i<=m;i++)
{
while(left<a[i].l)
{
judge[seq[left]]--;
if(judge[seq[left++]]==0)
ans--;
}
while(left>a[i].l)
{
if(!judge[seq[left-1]])
ans++;
judge[seq[--left]]++;
}
while(right<a[i].r)
{
if(!judge[seq[right+1]])
ans++;
judge[seq[++right]]++;
}
while(right>a[i].r)
{
judge[seq[right]]--;
if(!judge[seq[right--]])
ans--;
}
p[a[i].id]=ans;
}
for(int i=1;i<=m;i++)
printf("%d\n",p[i]);
return 0;
}
参考: modui