BZOJ2453:维护队列——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=2453

Description

你小时候玩过弹珠吗?
小朋友A有一些弹珠,A喜欢把它们排成队列,从左到右编号为1到N。为了整个队列鲜艳美观,小朋友想知道某一段连续弹珠中,不同颜色的弹珠有多少。当然,A有时候会依据个人喜好,替换队列中某个弹珠的颜色。但是A还没有学过编程,且觉得头脑风暴太浪费脑力了,所以向你来寻求帮助。

Input

输入文件第一行包含两个整数N和M。
第二行N个整数,表示初始队列中弹珠的颜色。
接下来M行,每行的形式为“Q L R”或“R x c”,“Q L R”表示A想知道从队列第L个弹珠到第R个弹珠中,一共有多少不同颜色的弹珠,“R x c”表示A把x位置上的弹珠换成了c颜色。

Output

对于每个Q操作,输出一行表示询问结果。

Sample Input

2 3
1 2
Q 1 2
R 1 2
Q 1 2

Sample Output

2
1

HINT

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

——————————————————————————————————————

一种单点修改的分块。

我们维护:

1.sum[i][j]:前j块i元素出现次数。

2.ans[i][j]:i~j块的颜色种数。

修改的时候我们显然sum数组可以O(sqrtN)修改成功,而ans数组通过暴力也可以在O(N/4)的复杂度中修改成功。

(实现方法看代码吧……)

剩下的查询就比较简单了(可能是分块分多的错觉)

#include<cstdio>
#include<queue>
#include<cctype>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=10010;
const int SQRTN=110;
const int M=11010;
const int INF=2147483647;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct question{
    char op[10];
    int l,r;
}q[N];
int n,m,lim,s,cnt,a[N],b[M],bl[SQRTN],br[SQRTN];
int sum[M][SQRTN],ans[SQRTN][SQRTN];
bool vis[M];
inline void initLSH(){
    sort(b+1,b+lim+1);
    lim=unique(b+1,b+lim+1)-b-1;
    for(int i=1;i<=n;i++){
    a[i]=lower_bound(b+1,b+lim+1,a[i])-b;
    }
    return;
}
inline int LSH(int x){return lower_bound(b+1,b+lim+1,x)-b;}
inline void intoblock(){
    for(int i=1;i<=n;i++){
        if(i%s==1){br[cnt]=i-1;bl[++cnt]=i;}
    }
    br[cnt]=n;bl[cnt+1]=n+1;
    return;
}
inline void init(){
    for(int i=1;i<=cnt;i++){
    for(int j=1;j<=lim;j++)sum[j][i]=sum[j][i-1];
        for(int j=bl[i];j<=br[i];j++){
            sum[a[j]][i]++;
        }
    }
    for(int i=1;i<=cnt;i++){
        for(int j=i;j<=cnt;j++){
        ans[i][j]=ans[i][j-1];
        for(int k=bl[j];k<=br[j];k++)vis[a[k]]=0;
        for(int k=bl[j];k<=br[j];k++){
        int t=a[k];
        if(!vis[t]){
            int c=sum[t][j-1]-sum[t][i-1];
            if(!c)ans[i][j]++;
            vis[t]=1;
        }
        }
        }
    }
    return;
}
inline void modify(int x,int c){
    if(a[x]==c)return;
    int now=(x-1)/s+1;
    int old=a[x];
    a[x]=c;
    int nw=a[x];
    for(int i=1;i<=now;i++){
        for(int j=now;j<=cnt;j++){
        if(sum[old][j]-sum[old][i-1]==1)ans[i][j]--;
        if(sum[nw][j]-sum[nw][i-1]==0)ans[i][j]++;
        }
    }
    for(int i=now;i<=cnt;i++)sum[old][i]--,sum[nw][i]++;
    return;
}
inline int query(int l,int r){
    int cur=0;
    if(r-l+1<=2*s){
    for(int i=l;i<=r;i++)vis[a[i]]=0;
    for(int i=l;i<=r;i++){
        int t=a[i];
        if(!vis[t]){
        cur++;
        vis[t]=1;
        }
    }
        return cur;
    }
    int L=(l-1)/s+1,R=(r-1)/s+1;
    cur+=ans[L+1][R-1];
    for(int i=l;i<=br[L];i++)vis[a[i]]=0;
    for(int i=bl[R];i<=r;i++)vis[a[i]]=0;
    for(int i=l;i<=br[L];i++){
    if(!vis[a[i]]){
        int c=sum[a[i]][R-1]-sum[a[i]][L];
        if(!c)cur++;
        vis[a[i]]=1;
    }
    }
    for(int i=bl[R];i<=r;i++){
    if(!vis[a[i]]){
        int c=sum[a[i]][R-1]-sum[a[i]][L];
        if(!c)cur++;
        vis[a[i]]=1;
    }
    }
    return cur;
}
int main(){
    n=read();m=read();s=sqrt(n);
    for(int i=1;i<=n;i++)a[i]=b[++lim]=read();
    for(int i=1;i<=m;i++){
    cin>>q[i].op;
    q[i].l=read(),q[i].r=read();
    if(q[i].op[0]=='R')b[++lim]=q[i].r;
    }
    initLSH();
    intoblock();
    init();
    for(int i=1;i<=m;i++){
    if(q[i].op[0]=='R')modify(q[i].l,LSH(q[i].r));
    else printf("%d\n",query(q[i].l,q[i].r));
    }
    return 0;
}

 

posted @ 2018-01-03 14:41  luyouqi233  阅读(295)  评论(0编辑  收藏  举报