莫队模板

转自:https://www.cnblogs.com/WAMonster/p/10118934.html
不需要修改值的莫队
题目链接:https://vjudge.net/problem/SPOJ-DQUERY#author=0
题意:求区间内不同的数的个数

把整个区间分块后排序,用双指针移动解决。
指针移动优化

while(l < ql) now -= !--cnt[aa[l++]];
while(l > ql) now += !cnt[aa[--l]]++;
while(r < qr) now += !cnt[aa[++r]]++;
while(r > qr) now -= !--cnt[aa[r--]];

普通排序函数
最好先用这个,如果超时再用优化排序 优化排序某些题可能会过不了 如hdu6333

int cmp(query a, query b) {
    return belong[a.l] == belong[b.l] ? a.r < b.r : belong[a.l] < belong[b.l];
}

还可以加读入输出优化
最好把分块的大小改成 n 2 3 \frac{2}{3} 32 会更快 n \sqrt{n} n 可能会退化

#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>
using namespace std;
const int maxn=15e5+5;
const int maxm=1e6+5;
int aa[maxn],cnt[maxm],ans[maxn],belong[maxn],now;
struct NODE
{
    int l,r,id;
} q[maxn];
int cmp(NODE a, NODE b)//玄学排序
{
    return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : ((belong[a.l] & 1) ? a.r < b.r : a.r > b.r);
}
int main()
{
    int n,m;
    scanf("%d",&n);
    int sz=sqrt(n);
    int bnum=ceil((double)n/sz);
    for(int i=1; i<=bnum; i++)
    {
        for(int j=(i-1)*sz+1; j<=i*sz; j++)
        {
            belong[j]=i;

        }
    }
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&aa[i]);
    }
    scanf("%d",&m);
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+1+m,cmp);
    int l=1,r=0;
    for(int i=1; i<=m; i++)
    {
        int ql=q[i].l,qr=q[i].r;
        while(l<ql)
        {
            --cnt[aa[l]];
            if(!cnt[aa[l]])
                now--;
            l++;
        }
        while(l>ql)
        {
            l--;
            if(!cnt[aa[l]])
                now++;
            ++cnt[aa[l]];
        }
        while(r<qr)
        {
            r++;
            if(!cnt[aa[r]])
                now++;
            ++cnt[aa[r]];
        }
        while(r>qr)
        {
            --cnt[aa[r]];
            if(!cnt[aa[r]])
                now--;
            r--;
        }
        ans[q[i].id]=now;
    }
    for(int i=1; i<=m; i++)
        printf("%d\n",ans[i]);
    return 0;
}

带单点修改的莫队
题目链接:https://vjudge.net/problem/HYSBZ-2120
单点修改就多了一个指针time,表示当前询问所在的时间顺序,每次查询时time移动并且修改cnt,同时更改值。
排序函数也需要修改一下。
time指针优化

        while(time < qt) {
            ++time;
            if(ql <= c[time].pos && c[time].pos <= qr) now -= !--cnt[a[c[time].pos]] - !cnt[c[time].color]++;
            swap(a[c[time].pos], c[time].color);
        }
        while(time > qt) {
            if(ql <= c[time].pos && c[time].pos <= qr) now -= !--cnt[a[c[time].pos]] - !cnt[c[time].color]++;
            swap(a[c[time].pos], c[time].color);
            --time;
        }
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>
using namespace std;
const int maxn=1e4+5;
const int maxm=1e6+5;
int a[maxn],cnt[maxm],ans[maxn],belong[maxn];
struct query
{
    int l,r,time,id;
} q[maxn];
struct modify
{
    int pos,color,last;
} c[maxn];
int cntq,cntc,n,m,sz,bnum;
int cmp(query a, query b)
{
    return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : ((belong[a.r] ^ belong[b.r]) ? belong[a.r] < belong[b.r] : a.time < b.time);
}
int main()
{
    scanf("%d%d",&n,&m);
    getchar();
    sz=pow(n,2.0/3.0);
    bnum=ceil((double)n/sz);
    for(int i=1; i<=bnum; i++)
    {
        for(int j=(i-1)*sz+1; j<=i*sz; j++)
        {
            belong[j]=i;
        }
    }
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
        getchar();
    }
    for(int i=1; i<=m; i++)
    {
        char ss[100];
        int x,y;
        scanf("%s",ss);
        getchar();
        scanf("%d%d",&x,&y);
        getchar();
        if(ss[0]=='Q')
        {
            q[++cntq].l=x;
            q[cntq].r=y;
            q[cntq].time=cntc;
            q[cntq].id=cntq;
        }
        else if(ss[0]=='R')
        {
            c[++cntc].pos=x;
            c[cntc].color=y;
        }
    }
    sort(q+1,q+cntq+1,cmp);
    int l=1,r=0,time=0,now=0;
    for(int i=1; i<=cntq; i++)
    {
        int ql=q[i].l,qr=q[i].r,qt=q[i].time;
        while(l<ql)
        {
            --cnt[a[l]];
            if(!cnt[a[l]])
                now--;
            l++;
        }
        while(l>ql)
        {
            l--;
            if(!cnt[a[l]])
                now++;
            ++cnt[a[l]];
        }
        while(r<qr)
        {
            r++;
            if(!cnt[a[r]])
                now++;
            ++cnt[a[r]];
        }
        while(r>qr)
        {
            --cnt[a[r]];
            if(!cnt[a[r]])
                now--;
            r--;
        }
        while(time<qt)
        {
            ++time;
            if(ql<=c[time].pos&&c[time].pos<=qr)
            {
                cnt[a[c[time].pos]]--;
                if(!cnt[a[c[time].pos]])
                    now--;
                if(!cnt[c[time].color])
                    now++;
                cnt[c[time].color]++;
            }
            swap(a[c[time].pos],c[time].color);
        }
        while(time>qt)
        {
            if(ql<=c[time].pos&&c[time].pos<=qr)
            {
                cnt[a[c[time].pos]]--;
                if(!cnt[a[c[time].pos]])
                    now--;
                if(!cnt[c[time].color])
                    now++;
                cnt[c[time].color]++;
            }
            swap(a[c[time].pos],c[time].color);
            time--;
        }
        ans[q[i].id]=now;
    }
    for(int i=1; i<=cntq; i++)
    {
        printf("%d\n",ans[i]);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值