bzoj 2120 数颜色 题解
转载请注明:http://blog.csdn.net/jiangshibiao/article/details/23990489
【原题】
2120: 数颜色
Time Limit: 6 Sec Memory Limit: 259 MBSubmit: 1201 Solved: 429
[Submit][Status]
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
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
4
3
4
HINT
对于100%的数据,N≤10000。M≤10000,改动操作不多于1000次。全部的输入数据中出现的全部整数均大于等于1且不超过10^6。
【分析】曾经用莫队算法做一直超时。
今天意外听说能够用非常神的暴力A过去,于是打算去试试。
【基础暴力代码】
#include<cstdio> #include<cstring> using namespace std; bool f[1000005]; int a[10005],l,r,n,m,i,j,ans; char c; int main() { scanf("%d%d",&n,&m); for (i=1;i<=n;i++) scanf("%d",&a[i]); for (i=1;i<=m;i++) { c=' ';while (c!='Q'&&c!='R') c=getchar(); scanf("%d%d",&l,&r); if (c=='R') {a[l]=r;continue;} ans=0;memset(f,0,sizeof(f)); for (j=l;j<=r;j++) if (!f[a[j]]) ans++,f[a[j]]=true; printf("%d\n",ans); } return 0; }
我们会发现,f数组的MEMSET太耗时间了。
怎么办呢?用凌神的标号法!
做到某个i操作时,从l到r扫描,若f[a[j]]还不是i就把他赋成i,并把ans加一。
但是这样还是超时啊。事实证明,当数组开得非常大时,寻址须要非常多时间。
我们能够把颜色离散一下,使每次判重的数组f的元素仅仅有11000(注意会改1000次),这样2s妥妥地A了。
【代码】
#include<cstdio> using namespace std; int f[11005],l,r,n,m,i,j,ans,p,d[10005]; int a[1000005]; char c; int main() { scanf("%d%d",&n,&m); for (i=1;i<=n;i++) { int o; scanf("%d",&o); if (!a[o]) a[o]=++p; d[i]=a[o]; } for (i=1;i<=m;i++) { c=' ';while (c!='Q'&&c!='R') c=getchar(); scanf("%d%d",&l,&r); if (c=='R') {if(!a[r])a[r]=++p;d[l]=a[r];continue;} ans=0; for (j=l;j<=r;j++) if (f[d[j]]!=i) ans++,f[d[j]]=i; printf("%d\n",ans); } return 0; }