题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=2120

思路

这道题可以用分块,可以用莫队,可以用数结……
然而我只会分块和莫队耶

分块

首先来讲一下分块的思路:
每个点记录一个pre代表这个颜色上一次出现的地点,显然[L,R]pre<L的就是第一次出现的颜色。
将数列分块,每一块块内排序,这样查询时只需二分寻找pre<L的点。
BZOJ交不过耶,是不是我常数太大了qwq

分块代码

#pragma GCC optimize(2)
//然而强制开O2也救不了我的大常数
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>

const int maxn=10000;
const int maxc=1000000;

int belong[maxn+10],n,m,color[maxn+10],size;
int pre[maxn+10],nowc[maxc+10],r[maxn+10];

int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

int main()
{
  n=read();
  m=read();
  size=(int)sqrt(n);
  for(register int i=1; i<=n; ++i)
    {
      color[i]=read();
      belong[i]=(i-1)/size+1;
    }
  for(register int i=1; i<=n; ++i)
    {
      r[i]=pre[i]=nowc[color[i]];
      nowc[color[i]]=i;
    }
  int k=n/size;
  for(register int i=1; i<=k; ++i)
    {
      std::sort(r+(i-1)*size+1,r+i*size+1);
    }
  if(n%size!=0)
    {
      std::sort(r+(n-n%size)+1,r+n+1);
    }
  while(m--)
    {
      char ch=getchar();
      while((ch!='Q')&&(ch!='R'))
        {
          ch=getchar();
        }
      int a,b;
      scanf("%d%d",&a,&b);
      if(ch=='Q')
        {
          int ans=0;
          if(belong[a]==belong[b])
            {
              for(register int i=a; i<=b; ++i)
                {
                  if(pre[i]<a)
                    {
                      ++ans;
                    }
                }
              printf("%d\n",ans);
            }
          else
            {
              int be=belong[a]*size;
              if(be>n)
                {
                  be=n;
                }
              for(register int i=a; i<=be; ++i)
                {
                  if(pre[i]<a)
                    {
                      ++ans;
                    }
                }
              be=(belong[b]-1)*size+1;
              for(register int i=be; i<=b; ++i)
                {
                  if(pre[i]<a)
                    {
                      ++ans;
                    }
                }
              for(register int i=belong[a]+1; i<belong[b]; ++i)
                {
                  ans+=std::lower_bound(r+(i-1)*size+1,r+i*size+1,a)-(r+(i-1)*size+1);
                }
              printf("%d\n",ans);
            }
        }
      else
        {
          color[a]=b;
          memset(nowc,0,sizeof nowc);
          for(register int i=1; i<=n; ++i)
            {
              r[i]=pre[i]=nowc[color[i]];
              nowc[color[i]]=i;
            }
          for(register int i=1; i<=k; ++i)
            {
              std::sort(r+(i-1)*size+1,r+i*size+1);
            }
          if(n%size!=0)
            {
              std::sort(r+n-n%size+1,r+n+1);
            }
        }
    }
  return 0;
}

莫队

这不就是裸的带修改莫队吗?(而且时间复杂度和常数似乎都优越一些耶)

莫队代码

#include <cstdio>
#include <cmath>
#include <algorithm>

const int maxn=10000;
const int maxc=1000000;

struct query
{
  int l,r,t,id;
};

int belong[maxn+10],n,m,color[maxn+10];
int nowl,nowr,nowt,ans[maxn+10],cntq,cntm,used[maxc+10],nowans;
int pos[maxn+10],prec[maxn+10],nowc[maxn+10];
query q[maxn+10];

bool cmp(const query &a,const query &b)
{
  if(belong[a.l]==belong[b.l])
    {
      if(belong[a.r]==belong[b.r])
        {
          return a.t<b.t;
        }
      return belong[a.r]<belong[b.r];
    }
  return belong[a.l]<belong[b.l];
}

inline int modify(int po,int op)
{
  int pr=used[color[po]];
  used[color[po]]+=op;
  if(pr==0)
    {
      ++nowans;
    }
  else if(used[color[po]]==0)
    {
      --nowans;
    }
  return 0;
}

inline int change_time(int po,int c)
{
  if((nowl<=po)&&(po<=nowr))
    {
      --used[color[po]];
      if(used[color[po]]==0)
        {
          --nowans;
        }
      color[po]=c;
      if(used[color[po]]==0)
        {
          ++nowans;
        }
      ++used[color[po]];
    }
  else
    {
      color[po]=c;
    }
  return 0;
}

inline int solve(int x)
{
  while(nowt<q[x].t)
    {
      ++nowt;
      change_time(pos[nowt],nowc[nowt]);
    }
  while(nowt>q[x].t)
    {
      change_time(pos[nowt],prec[nowt]);
      --nowt;
    }
  while(nowl<q[x].l)
    {
      modify(nowl++,-1);
    }
  while(nowl>q[x].l)
    {
      modify(--nowl,1);
    }
  while(nowr<q[x].r)
    {
      modify(++nowr,1);
    }
  while(nowr>q[x].r)
    {
      modify(nowr--,-1);
    }
  ans[q[x].id]=nowans;
  return 0;
}

int main()
{
  scanf("%d%d",&n,&m);
  for(register int i=1; i<=n; ++i)
    {
      scanf("%d",&color[i]);
    }
  for(register int i=1; i<=m; ++i)
    {
      char ch[10];
      int a,b;
      scanf("%s%d%d",ch,&a,&b);
      if(ch[0]=='Q')
        {
          q[++cntq].l=a;
          q[cntq].r=b;
          q[cntq].t=cntm;
          q[cntq].id=cntq;
        }
      else
        {
          pos[++cntm]=a;
          prec[cntm]=color[a];
          nowc[cntm]=b;
          color[a]=b;
        }
    }
  int size=(int)sqrt(cntq);
  for(register int i=1; i<=n; ++i)
    {
      belong[i]=(i-1)/size+1;
    }
  std::sort(q+1,q+cntq+1,cmp);
  nowl=nowr=nowans=used[color[1]]=1;
  nowt=cntm;
  for(register int i=1; i<=cntq; ++i)
    {
      solve(i);
    }
  for(register int i=1; i<=cntq; ++i)
    {
      printf("%d\n",ans[i]);
    }
  return 0;
}