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
次*
右指针移动:
n
次 *
时间指针移动:
(n13)2
次 *
n
的距离=
于是复杂度
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;
}