ACM_莫队算法

区间查询神器
莫队算法:
考虑优化,如何尽可能的利用已经求过的值,这是莫队算法的核心

但如何使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 莫队凉了

猜你喜欢

转载自blog.csdn.net/qq_38677814/article/details/80867603