P1903 [国家集训队]数颜色 (带修改莫队)

题目描述

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

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

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

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

输入输出格式

输入格式:

第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。

第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。

第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

输出格式:

对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。

输入输出样例

输入样例#1: 复制
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
输出样例#1: 复制
4
4
3
4

说明

对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。

来源:bzoj2120

  1 //感谢xxy dalao的指导, 虽然不得不看了blog才A了这道题。。。。 
  2 
  3 //但是为什么他们一个 dfs 10+ ms就过,我却跑了100+ ?。。。。。 
  4 
  5 #include<iostream>
  6 #include<cstdio>
  7 #include<cstring>
  8 #include<cmath>
  9 #include<algorithm>
 10 using namespace std;
 11 
 12 const int N=1e4+5;
 13 const int M=1e6+5;
 14 
 15 int n,m;
 16 int a[N];
 17 int ans;
 18 int belong[N];
 19 int c[M];
 20 int query_cnt,modify_cnt;
 21 struct Query    //询问操作 
 22 {
 23     int ans;    //这次询问的答案 
 24     int l,r;    //这次询问的左右区间 
 25     int tim;    //这是第几次询问,便于最后将询问排序输出ans 
 26     int tim_modify;        //记录这次询问是在第几次修改之后的,便于修改和撤销 
 27 }query[N];
 28 struct Modify    //修改操作 
 29 {
 30     int tim;    //这是第几次修改操作 
 31     int pos,col,pre_col;    //修改的位置、修改之前的颜色、修改之后的颜色 
 32 }modify[N];
 33 
 34 int read()
 35 {
 36     char c=getchar();int num=0;
 37     for(;!isdigit(c);c=getchar())
 38         if(c=='Q')
 39             return 1;
 40         else if(c=='R')
 41             return 2;
 42     for(;isdigit(c);c=getchar())
 43         num=num*10+c-'0';
 44     return num;
 45 }
 46 
 47 bool cmp1(const Query &a,const Query &b)    //将询问操作排序 
 48 {
 49     if(belong[a.l]==belong[b.l])    //为了保证效率,按照三个关键字排序,前两个和普通的不带修改的莫队一样,第三个关键字是修改时间 
 50         if(belong[a.r]==belong[b.r])
 51             return a.tim_modify<b.tim_modify;
 52         else
 53             return belong[a.r]<belong[b.r];
 54     return belong[a.l]<belong[b.l];
 55 }
 56 
 57 bool cmp2(const Query &a,const Query &b)    //将query按照询问时间排序,便于输出ans 
 58 {
 59     return a.tim<b.tim;
 60 }
 61 
 62 int main()
 63 {
 64     n=read(),m=read();
 65     int size=sqrt(n);
 66     for(int i=1;i<=n;++i)
 67         a[i]=read(),belong[i]=(i-1)/size+1;
 68     for(int i=1,type;i<=m;++i)
 69     {
 70         type=read();
 71         if(type==1)
 72         {
 73             ++query_cnt;
 74             query[query_cnt].l=read();
 75             query[query_cnt].r=read();
 76             query[query_cnt].tim=query_cnt;        //记录这是第几次询问 
 77             query[query_cnt].tim_modify=modify_cnt;        //记录当前询问是在第几次修改之后 
 78         }
 79         else
 80         {
 81             ++modify_cnt;
 82             modify[modify_cnt].pos=read();
 83             modify[modify_cnt].col=read();    //修改之后的值 
 84             modify[modify_cnt].tim=modify_cnt;
 85             modify[modify_cnt].pre_col=a[modify[modify_cnt].pos];    //记录修改之前的值 
 86             a[modify[modify_cnt].pos]=modify[modify_cnt].col;    //修改 
 87         }
 88     }
 89     for(int i=modify_cnt;i;--i)        //把修改了的a数组还原回去 
 90         a[modify[i].pos]=modify[i].pre_col;
 91     sort(query+1,query+query_cnt+1,cmp1);
 92     int now_modify=0,l=1,r=0;
 93     for(int i=1;i<=query_cnt;++i)        //当时被卡在了修改和撤销上,没理解透彻,不知道该不该更新ans,因为当时觉得如果更新了ans的话会让后边在移动左右端点更新值得时候重复,其实不然,因为如果当前修改的值在当前询问的区间中,那么它是不会被更改的(因为改到左右端点的时候就停止了,不会来改它),如果不在当前询问的区间内,那么在移动端点的时候,被+1的ans会被还原回去(-1),但是被-1的ans是不会变的,因为它已经没了(滑稽) 
 94     {
 95         if(query[i].tim_modify>now_modify)    //当前询问在上次修改操作之后,往后修改 
 96         {
 97             for(int j=now_modify+1;j<=query[i].tim_modify;++j)
 98             {
 99                 if(modify[j].pos>=l&&modify[j].pos<=r)        //如果修改的位置在上次询问的区间内,更新 
100                 {
101                     --c[modify[j].pre_col];
102                     if(!c[modify[j].pre_col])
103                         --ans;
104                     if(!c[modify[j].col]) 
105                         ++ans;
106                     ++c[modify[j].col];
107                 }
108                 a[modify[j].pos]=modify[j].col;        //修改 
109             }
110         }
111         if(query[i].tim_modify<now_modify)        //当前询问在上次修改操作之前,撤销修改 
112         {
113             for(int j=now_modify;j>query[i].tim_modify;--j)
114             {
115                 if(modify[j].pos>=l&&modify[j].pos<=r)
116                 {
117                     --c[modify[j].col];        //把修改后的数的值还原回去 
118                     if(!c[modify[j].col])
119                         --ans;
120                     if(!c[modify[j].pre_col])    //被修改了的数的值还原回去 
121                         ++ans;
122                     ++c[modify[j].pre_col];
123                 }
124                 a[modify[j].pos]=modify[j].pre_col;        //撤销 
125             }
126         }
127         if(l<query[i].l)
128         {
129             for(int j=l;j<query[i].l;++j)
130             {
131                 --c[a[j]];
132                 if(!c[a[j]])
133                     --ans;
134             }
135         }
136         if(l>query[i].l)
137         {
138             for(int j=query[i].l;j<l;++j)
139             {
140                 if(!c[a[j]])
141                     ++ans;
142                 ++c[a[j]];
143             }
144         }
145         if(r<query[i].r)
146         {
147             for(int j=r+1;j<=query[i].r;++j)
148             {
149                 if(!c[a[j]])
150                     ++ans;
151                 ++c[a[j]];
152             }
153         }
154         if(r>query[i].r)
155         {
156             for(int j=query[i].r+1;j<=r;++j)
157             {
158                 --c[a[j]];
159                 if(!c[a[j]])
160                     --ans;
161             }
162         }
163         l=query[i].l,r=query[i].r;    //更新查询区间 
164         query[i].ans=ans;    //记录这次查询的答案 
165         now_modify=query[i].tim_modify;        //更新最新一次修改时间 
166     }
167     sort(query+1,query+query_cnt+1,cmp2);
168     for(int i=1;i<=query_cnt;++i)
169     {
170         printf("%d
",query[i].ans);
171     }
172     return 0;
173 }
原文地址:https://www.cnblogs.com/lovewhy/p/8439736.html