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支画笔中共有几种不同颜色的画笔。
输入输出样例
说明
对于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\n",query[i].ans); 171 } 172 return 0; 173 }