bzoj2453 维护队列 & bzoj2120 数颜色 (带修改莫队)

bzoj2453 维护队列 & bzoj2120 数颜色

原题地址
http://www.lydsy.com/JudgeOnline/problem.php?id=2453
http://www.lydsy.com/JudgeOnline/problem.php?id=2120

题意:
小朋友A有一些弹珠,A喜欢把它们排成队列,从左到右编号为1到N。
小朋友想知道某一段连续弹珠中,不同颜色的弹珠有多少。
A有时候会依据个人喜好,替换队列中某个弹珠的颜色。

数据范围
1 ≤ N ≤ 10000, 1 ≤ M ≤ 10000,小朋友A不会修改超过1000次,所有颜色均用1到10^6的整数表示。

题解:
带修改莫队裸题。
相比不带修多了一维时间(在第几个修改之后)。
块大小 n23 ,块个数 n13
排序按照 L所在快,R所在块,时间(在第几个修改之后) 排序。

复杂度证明:
左指针移动: n 次*n23的距离= O(nn23) = O(n53)
右指针移动: n 次 * n23的距离+ n13 次 * n23 的距离= O(nn23+n13n) = O(n53)
时间指针移动: (n13)2 次 * n 的距离=O((n13)2n)= O(n53)
于是复杂度 O(n53)

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=10000; 
struct query
{
    int l,r,t,id;   
}Q[N];
struct modify
{
    int pos,pre,nxt;
}M[N];
int n,m,col[N],st[N],ans[N],block,ret=0,qs,md,cnt[1000006];
int blo(int x)
{
    return(x-1)/block+1;
}
bool cmp(const query &A,const query &B)
{
    if(blo(A.l)!=blo(B.l)) return A.l<B.l;
    else if(blo(A.r)!=blo(B.r)) return A.r<B.r;
    else return A.t<B.t;
}
void add(int x) {x=col[x]; if(!cnt[x]) ret++; cnt[x]++;}
void erase(int x) {x=col[x]; cnt[x]--; if(cnt[x]==0)  ret--;}
void change(int tim,int l,int r,int opt)
{
    int pos=M[tim].pos; int pre=M[tim].pre; int now=M[tim].nxt;
    if(pos>=l&&pos<=r) erase(pos);
    col[pos]=(opt>0)? now:pre;
    if(pos>=l&&pos<=r) add(pos);
}
void moto()
{
    int lf=1; int rg=0; int cur=0; ret=0;
    for(int i=1;i<=qs;i++)
    {
        while(rg<Q[i].r) add(++rg);
        while(lf>Q[i].l) add(--lf);
        while(lf<Q[i].l) erase(lf++);
        while(rg>Q[i].r) erase(rg--);
        while(cur<Q[i].t) change(++cur,Q[i].l,Q[i].r,1);
        while(cur>Q[i].t) change(cur--,Q[i].l,Q[i].r,-1);
        ans[Q[i].id]=ret;
    }

}
int main()
{
    scanf("%d%d",&n,&m);
    block=sqrt(n);
    for(int i=1;i<=n;i++) {scanf("%d",&col[i]);st[i]=col[i];}
    qs=0,md=0;
    for(int i=1;i<=m;i++)
    {
        char opt[5]; int x,y;
        scanf("%s",opt); scanf("%d%d",&x,&y);
        if(opt[0]=='Q')
        {
            Q[++qs].l=x;Q[qs].r=y; Q[qs].t=md; Q[qs].id=qs;
        }
        else 
        {
            M[++md].pos=x; M[md].pre=col[x]; M[md].nxt=y; col[x]=y;
        }
    }
    for(int i=1;i<=n;i++) col[i]=st[i];
    sort(Q+1,Q+qs+1,cmp);
    moto();
    for(int i=1;i<=qs;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、付费专栏及课程。

余额充值