zoukankan      html  css  js  c++  java
  • [国家集训队]数颜色 / 维护队列

    题目描述

    墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:

    1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。

    2、 R P Col 把第P支画笔替换为颜色Col。

    为了满足墨墨的要求,你知道你需要干什么了吗?

    对于所有数据,n,m133333

    所有的输入数据中出现的所有整数均大于等于1且不超过10^6。

    题解

    待修改的莫队,一开始以为很难,结果好像还比较简单。就多记录一个当前询问是在第几个修改之后,莫队过程中也多记录一个当前在第几个修改后,改多了就改回去,改少了就修改就好了。

    cmp函数也要再加一维时间,不过第二个判断也变成了右端点的块,而不只是两点相不相等(没看清楚一直T)。

    还有只有修改在询问区间采取统计答案!!!!!

    至于搞修改有两种:

    1.记录这个前x位置的数,然后就分修改和撤销修改稿

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=150000;
    const int maxm=1000005;
    int n,m,cnt,num;
    int nowl=1,nowr=0,nownum=0,ret;
    int a[maxn],b[maxn];
    int size,pos[maxn];
    int ans[maxn],vis[maxm];
    struct query{
      int l,r,id,num;
    }q[maxn];
    struct modify{
      int x,y,pre,num;
    }mo[maxn];
    
    template<class T>inline void read(T &x){
        x=0;char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    }
    
    bool cmp(query a,query b){
      if(pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
      if(pos[a.r]!=pos[b.r])  return pos[a.r]<pos[b.r];
      return a.num<b.num;
    }
    
    inline void add(int x){
      if(++vis[a[x]]==1) ret++;
    }
    
    inline void del(int x){
      if(!--vis[a[x]]) ret--;
    }
    
    inline void go(int i){
      if(nowl<=mo[i].x&&mo[i].x<=nowr){
        if(!--vis[a[mo[i].x]]) ret--;
        if(++vis[mo[i].y]==1) ret++;
      }
      a[mo[i].x]=mo[i].y;
    }
    
    inline void back(int i){
      if(nowl<=mo[i].x&&mo[i].x<=nowr){
        if(!--vis[a[mo[i].x]]) ret--;
        if(++vis[mo[i].pre]==1) ret++;
      }
      a[mo[i].x]=mo[i].pre;
    }
    
    int main(){
      read(n);read(m);
      size=pow(n,2.0/3);
      for(int i=1;i<=n;i++){
        read(a[i]);b[i]=a[i];
        pos[i]=(i-1)/size+1;
      }
      for(int i=1;i<=m;i++){
        char op[2];
        scanf("%s",op);
        if(op[0]=='Q'){
          int l,r;
          read(l);read(r);
          ++cnt;
          q[cnt]=(query){l,r,cnt,num};
        }
        else {
          int x,y;
          read(x);read(y);
          ++num;
          mo[num]=(modify){x,y,b[x],num};
          b[x]=y;
        }
      }
      sort(q+1,q+cnt+1,cmp);
      for(int i=1;i<=cnt;i++){
        while(nowl>q[i].l) add(--nowl);
        while(nowr<q[i].r) add(++nowr);
        while(nowl<q[i].l) del(nowl++);
        while(nowr>q[i].r) del(nowr--);
        while(nownum<q[i].num) go(++nownum);
        while(nownum>q[i].num) back(nownum--);
        ans[q[i].id]=ret;
      }
      for(int i=1;i<=cnt;i++) printf("%d
    ",ans[i]);
    }
    莫队1

    2.然后就是可以发现经过一次该修改后,下一次经过一定是改回去,所以交换修改的值和原序列的值即可。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=150000;
    const int maxm=1000005;
    int n,m,cnt,num;
    int nowl=1,nowr=0,nownum=0,ret;
    int a[maxn];
    int size,pos[maxn];
    int ans[maxn],vis[maxm];
    struct query{
      int l,r,id,num;
    }q[maxn];
    struct modify{
      int x,y,pre,num;
    }mo[maxn];
    
    template<class T>inline void read(T &x){
        x=0;char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    }
    
    bool cmp(query a,query b){
      if(pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
      if(pos[a.r]!=pos[b.r])  return pos[a.r]<pos[b.r];
      return a.num<b.num;
    }
    
    inline void add(int x){
      if(++vis[a[x]]==1) ret++;
    }
    
    inline void del(int x){
      if(!--vis[a[x]]) ret--;
    }
    
    inline void change(int i){
      if(nowl<=mo[i].x&&mo[i].x<=nowr){
        if(!--vis[a[mo[i].x]]) ret--;
        if(++vis[mo[i].y]==1) ret++;
      }
      swap(a[mo[i].x],mo[i].y);
    }
    
    int main(){
      read(n);read(m);
      size=pow(n,2.0/3);
      for(int i=1;i<=n;i++){
        read(a[i]);
        pos[i]=(i-1)/size+1;
      }
      for(int i=1;i<=m;i++){
        char op[2];
        scanf("%s",op);
        if(op[0]=='Q'){
          int l,r;
          read(l);read(r);
          ++cnt;
          q[cnt]=(query){l,r,cnt,num};
        }
        else {
          int x,y;
          read(x);read(y);
          ++num;
          mo[num]=(modify){x,y,num};
        }
      }
      sort(q+1,q+cnt+1,cmp);
      for(int i=1;i<=cnt;i++){
        while(nowl>q[i].l) add(--nowl);
        while(nowr<q[i].r) add(++nowr);
        while(nowl<q[i].l) del(nowl++);
        while(nowr>q[i].r) del(nowr--);
        while(nownum<q[i].num) change(++nownum);
        while(nownum>q[i].num) change(nownum--);
        ans[q[i].id]=ret;
      }
      for(int i=1;i<=cnt;i++) printf("%d
    ",ans[i]);
    }
    莫队2

    然后这道题也可以用树套树做(我没写),由HH的项链可以得到答案就是$sum_{i=l}^{r}pre[i]<l$,pre是上一个与a[i]值相同的位置,用树状数组记录原序列,每个节点开一颗值域线段树记录区间上pre。

    修改就随便yy,知道哪些的pre会改就好了。不过维护pre和next很麻烦,所以用set或者splay维护(这就是为什么我不想写的原因)。

    反正纯属xbb,随便看看就行。

  • 相关阅读:
    场景设计法
    判定表驱动分析方法
    错误推测法
    Ubuntu软件包管理命令全面集锦
    MySql模糊查询
    VC++ 列表控件的使用方法
    Java笔记原生数据类型【二】
    DEDECMS 关键字不能小于2个字节!
    Linux 使用yum install安装mysql登陆不上解决办法
    PHP数据学习-二维数组【3】
  • 原文地址:https://www.cnblogs.com/sto324/p/11420528.html
Copyright © 2011-2022 走看看