2120: 数颜色
题目链接
Time Limit: 6 Sec Memory Limit: 259 MB
Submit: 8304 Solved: 3417
[Submit][Status][Discuss]
Description
墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令: 1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。 2、 R P Col 把第P支画笔替换为颜色Col。为了满足墨墨的要求,你知道你需要干什么了吗?
Input
第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。
Output
对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。
Sample Input
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
Sample Output
4
4
3
4
HINT
对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。
2016.3.2新加数据两组by Nano_Ape
Source
题解
分块或者莫队或者树套树。
其中蒟蒻只会分块和莫队。
分块,首先要考虑的就是怎么判断颜色个数。
有点像是欧拉筛的感觉,我们定义一个
Li
L
i
和
Ri
R
i
分别表示
i
i
左边和右边第一个和 相同的颜色的位置。
每次询问
QLR
Q
L
R
无疑便是判断有多少个
Li<L
L
i
<
L
,这样就可以避免重算和少算了。
怎么求呢?我们可以对
Li
L
i
排序,用二分查找(复杂度变为
N∗log(N)
N
∗
l
o
g
(
N
)
)。但是不能每次把
L
L
到 之间的
Li
L
i
都排序,因为这样总复杂度就是
M∗N
M
∗
N
。那么,我们就使得每个块都保持有序的状态,每次修正过某个块中的
Li
L
i
就排一趟序。
然后就是修改的时候要注意一些细节(详见代码吧)。
莫队,那么这就是典型的带修改的莫队了。
我们把每次修改也存起来,查询带一个 tim。表示这个查询是在第 tim 修改后进行的。
指针 T 表示当前进行到第 T 次操作,那么我们的操作也可进可退,和 L、R 一个道理,排序的时候 tim 也要考虑进去。
代码
分块
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define gtc() getchar()
using namespace std;
const int maxn=5e4+4,maxm=505;//洛谷上数据范围要大一点
int n,m,T,len,p[maxn],f[maxn<<1],lst[maxn<<1],L[maxn],R[maxn];
int read()
{
int ret=0,f=1;char ch=gtc();
while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gtc();}
while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=gtc();
return ret*f;
}
struct js1{
int L,R,w[maxm],s[maxm];
int FIN(int LL)
{
int le=1,ri=len,mid=le+ri>>1;
for (;le<=ri;mid=le+ri>>1)
if (w[mid]<LL) le=mid+1;else ri=mid-1;
return ri;
}
}c[maxm];
struct js2{
bool pd;
int x,y;
void gt()
{
char ch=gtc();
while (ch!='Q'&&ch!='R') ch=gtc();
pd=(ch!='Q');
x=read();y=read();
if (pd) f[++f[0]]=y;
}
}a[maxn];
void SORT(js1 *c){for (int j=c->L;j<=c->R;j++)c->w[j-c->L+1]=L[j];sort(c->w+1,c->w+len+1);}
void SORT1(js1 *c){for (int j=c->L;j<=c->R;j++)c->s[j-c->L+1]=p[j];sort(c->s+1,c->s+len+1);}
int fin(int x,int *f,int m,int n)
{
for (int L=m,R=n,mid=L+R>>1;L<=R;mid=L+R>>1)
if (x>f[mid]) L=mid+1;else
if (x<f[mid]) R=mid-1;else
return mid;
return -1;
}
int get(int x){return (x-1)/len+1;}
void change(int x,int cl)
{
if (p[x]==cl) return;
R[L[x]]=R[x];
L[R[x]]=L[x];SORT(&c[get(R[x])]);
int now=get(x),lst=-1;
L[x]=R[x]=0;SORT(&c[now]);
for (int i=x+1;i<=c[now].R;i++)
if (p[i]==cl) {L[i]=x;R[x]=i;SORT(&c[now]);lst=-10;break;}
if (lst!=-10)
{
for (int i=now+1;i<=T;i++)
if (fin(cl,c[i].s,1,len)>0) {lst=i;break;}
if (lst>0) for (int i=c[lst].L;i<=c[lst].R;i++)
if (p[i]==cl) {L[i]=x;R[x]=i;SORT(&c[lst]);break;}
}
lst=-1;
for (int i=x-1;i>=c[now].L;i--)
if (p[i]==cl) {R[i]=x;L[x]=i;SORT(&c[now]);lst=-10;break;}
if (lst!=-10)
{
for (int i=now-1;i>=1;i--)
if (fin(cl,c[i].s,1,len)>0) {lst=i;break;}
if (lst>0) for (int i=c[lst].R;i>=c[lst].L;i--)
if (p[i]==cl) {R[i]=x;L[x]=i;SORT(&c[now]);break;}
}
p[x]=cl;SORT1(&c[now]);
}
int query(int le,int ri)
{
int Lnow=get(le),Rnow=get(ri),sum=0;
for (int i=min(c[Lnow].R,ri);i>=le;i--) if (L[i]<le) sum++;if (Lnow!=Rnow)
for (int i=max(c[Rnow].L,le);i<=ri;i++) if (L[i]<le) sum++;
for (int i=Lnow+1;i<Rnow;i++)
sum+=c[i].FIN(le);
return sum;
}
int main()
{
n=read();m=read();
for (int i=1;i<=n;i++) p[i]=f[i]=read();f[0]=n;
for (int i=1;i<=m;i++) a[i].gt();
sort(f+1,f+f[0]+1);int tot=f[0];f[0]=1;
for (int i=2;i<=tot;i++)
if (f[i]!=f[i-1]) f[++f[0]]=f[i];
for (int i=1;i<=n;i++) p[i]=fin(p[i],f,1,f[0]);
for (int i=1;i<=m;i++) if (a[i].pd) a[i].y=fin(a[i].y,f,1,f[0]);
T=sqrt(n);len=n/T+(n%T!=0);
memset(lst,0,sizeof lst);
for (int i=1,now=0;i<=n;i++)
{
L[i]=lst[p[i]];
lst[p[i]]=i;
if (i%len==0||i==n)
{
now++;
c[now].L=c[now-1].R+1;c[now].R=i;
SORT(&c[now]);SORT1(&c[now]);
}
}
memset(lst,0,sizeof lst);
for (int i=n;i>=1;i--)
{
R[i]=lst[p[i]];
lst[p[i]]=i;
}
for (int i=1;i<=m;i++)
if (a[i].pd) change(a[i].x,a[i].y);
else printf("%d\n",query(a[i].x,a[i].y));
return 0;
}
莫队
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=50005,maxm=maxn,maxt=1005;
int n,m,ma,mt,cnt,L,R,T,num[maxn],sum[(int)1e6+2],cl[maxn],ans,answer[maxn];
struct js1{
int L,R,tim,id,nl,nr;
bool operator <(const js1 b)const{return (nl==b.nl)?((nr==b.nr)?tim<b.tim:R<b.R):L<b.L;}
}a[maxn];
struct js2{
int x,c;
}b[maxn];
int read()
{
int ret=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
void movetime(js2 &a)
{
if (L<=a.x&&a.x<=R)
{
if (!(--sum[cl[a.x]])) --ans;
if ((++sum[a.c])==1) ++ans;
}
swap(cl[a.x],a.c);
}
int main()
{
n=read(),m=read();cnt=pow(n,0.666666);
for (int i=1;i<=n;++i) num[i]=i/cnt,cl[i]=read();
for (int i=1;i<=m;++i)
{
char ch[5];scanf("%s",ch);
if (ch[0]=='Q'){a[++ma]=(js1){read(),read(),mt,ma};a[ma].nl=num[a[ma].L];a[ma].nr=num[a[ma].R];}
else b[++mt]=(js2){read(),read()};
}
sort(a+1,a+ma+1);L=1;
for (int i=1;i<=ma;++i)
{
while(T<a[i].tim)movetime(b[++T]);
while(T>a[i].tim)movetime(b[T--]);
while(L<a[i].L) if(!--sum[cl[L++]]) ans--;
while(L>a[i].L) if(!sum[cl[--L]]++) ans++;
while(R<a[i].R) if(!sum[cl[++R]]++) ans++;
while(R>a[i].R) if(!--sum[cl[R--]]) ans--;
answer[a[i].id]=ans;
}
for (int i=1;i<=ma;++i) printf("%d\n",answer[i]);
return 0;
}