bzoj-2120 数颜色

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 左边和右边第一个和 i 相同的颜色的位置。
每次询问 QLR Q L R 无疑便是判断有多少个 LiL L i < L ,这样就可以避免重算和少算了。
怎么求呢?我们可以对 Li L i 排序,用二分查找(复杂度变为 Nlog(N) N ∗ l o g ( N ) )。但是不能每次把 L L R 之间的 Li L i 都排序,因为这样总复杂度就是 MN 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值