洛谷Oj-P1972 [SDOI2009]HH的项链-莫队

问题描述:
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
AC代码:

struct query//一个查询
{
    int l;//左端点
    int r;/右端点
    int id;//第id个查询
    int b;//在第b个块中
    int ans;//答案
};
query q[200010];
int a[500000];//原数组
int cnt[1000010];//记录数字出现的次数
int curL,curR;//指针
int k;//当前区间内数字的种类数
bool cmp1(const query &a,const query &b)
{
    if(a.b == b.b)
        return a.r < b.r;//块内按右端点排序
    return a.b < b.b;//块间排序
}
bool cmp2(const query &a,const query &b)//恢复初始的查询顺序
{
    return a.id < b.id;
}
void add(int id)//将a[id]加入当前区间
{
    cnt[a[id]]++;//计数+1
    if(cnt[a[id]] == 1)//如果为1,说明是第一次出现
        k++;
    return;
}
void del(int id)//将a[id]从当前区间删除
{
    cnt[a[id]]--;//计数-1
    if(cnt[a[id]] == 0)//如果为0,说明区间内已无此数
        k--;
    return;
}
inline void solve(query &q)//莫队算法的核心,注意需要引用
{
    //l、r为当前的查询
    int l = q.l;
    int r = q.r;
    while(curL < l)//如果当前左指针在l的左边,cur == l时退出循环
    {
        del(curL);//先将所指元素出现的次数-1
        curL++;//再移动指针
    }
    while(curL > l)//如果当前左指针在l的右边,cur == l时退出循环
    {
        curL--;//先移动指针
        add(curL);//再将所指元素出现的次数+1
    }
    while(curR < r)//同上,注意自增自减的顺序
    {
        curR++;
        add(curR);
    }
    while(curR > r)//同上
    {
        del(curR);
        curR--;
    }
    q.ans = k;
}
int main()
{
    int n;
    cin >> n;
    int sz = sqrt(n);//块的大小
    for(int i = 1; i <= n; ++i)
        scanf("%d",&a[i]);
    int m;
    cin >> m;
    for(int i = 1; i <= m; ++i)//处理每一个查询
    {
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].b = q[i].l / sz;//块的编号
        q[i].id = i;
    }
    sort(q + 1,q + m + 1,cmp1);
    //初始化左右指针
    curL = 1;
    curR = 0;
    for(int i = 1; i <= m; ++i)
        solve(q[i]);//求解
    sort(q + 1,q + m + 1,cmp2);
    for(int i = 1; i <= m; ++i)//输出
        cout << q[i].ans << endl;
    return 0;
}

解决方法:
这道题是一道经典的莫队题,如果暴力地去做的话,之前获得的信息没有被利用,造成了浪费,时间开销就大。如果将这若干个查询进行处理,使得我们能较好的利用上一次求解获得的信息,那么复杂度就会得到改善。
对这些查询进行离线处理,根据查询的左端点进行排序,分成若干个块,每个块内则按照查询的右端点由小到大进行排序,这样就避免了指针前前后后乱跑,从而节省了时间
cnt数组与k配合地不错
如果数据的值比较大,而个数比较少,则考虑对其进行离散化

阅读更多
版权声明:欢迎转载 https://blog.csdn.net/suntengnb/article/details/79947354
个人分类: 莫队算法
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭