区间查询神器
莫队算法:
考虑优化,如何尽可能的利用已经求过的值,这是莫队算法的核心
但如何使L和R移动的次数尽可能少而覆盖所有的询问,是莫队所要解决的
比如n=9,有以下的询问:
2 3
1 4
4 5
1 6
7 9
8 9
5 8
6 8
对于n=9,我们以根号n为每个块block的大小,这里block=3.
那么我们把1~3分成一组,4~6,7~9.
对于每一个询问(L,r),我们以L的范围来决定这个询问在哪一个块。
然后每一个独自的块内,我们让询问r更小的排在更前面。
那么上面的询问就可以分组成:
(2,3)/(1,4)/(1,6)和
(4,5)/(5,8)/(6,8)和
(7,9)/(8,9)
分块的意义就在于此
题目背景
无
题目描述
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
输入输出格式
输入格式:
第一行:一个整数N,表示项链的长度。
第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为0 到100000 之间的整数)。
第三行:一个整数M,表示HH 询问的个数。
接下来M 行:每行两个整数,L 和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
输出格式:
M 行,每行一个整数,依次表示询问对应的答案。
输入输出样例
输入样例#1:
6
1 2 3 4 3 5
3
1 2
3 5
2 6
输出样例#1:
2
2
4
#include <bits/stdc++.h>
using namespace std;
#define MAXN (1000000+10)
struct node
{
int l,r,ord,id,ans;
}Ask[MAXN];
int l,r,now,n,m;
int cnt[MAXN],a[MAXN];
bool cmp1(node a,node b){return a.id==b.id?a.r<b.r:a.id<b.id;}
bool cmp2(node a,node b){return a.ord<b.ord;}
void Del(int x)
{
cnt[a[x]]--;//个数--
if (!cnt[a[x]]) now--;//减为0,总数--
}
void Ins(int x)
{
if (!cnt[a[x]]) now++;//个数为0 插入,总数++
cnt[a[x]]++;//个数++
}
void MO(int x)
{
int L=Ask[x].l,R=Ask[x].r;
while (l>L) Ins(--l);//大于当前左端点,往左跑,遇到的全给我插进去
while (l<L) Del(l++);//小于左端点,往右跑,遇到的全给我扔掉
while (r>R) Del(r--);//大于右端点,往左跑,遇到的全部删掉
while (r<R) Ins(++r);//小于右端点 往右跑 遇到的全部插进去
Ask[x].ans=now;//得到当前不同的数量
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
int len=sqrt(n);
for (int i=1;i<=n;++i)
cin>>a[i];
cin>>m;
for (int i=1;i<=m;++i)
{
cin>>Ask[i].l>>Ask[i].r;
Ask[i].id=Ask[i].l/len;
Ask[i].ord=i;
}
sort(Ask+1,Ask+m+1,cmp1);//区间排序
for (int i=1;i<=m;++i) MO(i);
sort(Ask+1,Ask+m+1,cmp2);//变回原来的样子
for (int i=1;i<=m;++i)
printf("%d\n",Ask[i].ans);
return 0;
}
后来这题数据达到了100w 莫队凉了