2120: 数颜色
Time Limit: 6 Sec Memory Limit: 259 MBSubmit: 7536 Solved: 3060
[Submit][Status][Discuss]
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
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
Sample Output
4
3
4
HINT
对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。
2016.3.2新加数据两组by Nano_Ape
分析:带修改的莫队裸题.
如果没有修改操作,就是莫队裸题了,有修改操作就是带修改的莫队裸题了
具体方法就是将时间(或者说是原操作的顺序、编号)加进询问,使得询问变为一个三
维空间中的点。之前没有修改操作,那么询问的时间是没有用的,先询问与后询问并没有什
么区别;但现在有了修改,后询问的答案可能会受前面修改的影响。而现在询问之间互相转
移时,前两维是询问操作的左右两端点,这与之前是一致的,而第三维时间的转移则看起来
不那么直观。实际上第三维的转移要分情况考虑:
若是从时间小的询问转移到时间大的询问,则我们把时间这一维从t1转移至t2就是在进行修改,对于所经过的所有时间点,若当前时间点是一次修改则我们要对应地执行它,若它修改的值的位置此时存在于我们所维护的答案的那个区间中,则还需要判断答案是否改变。
若是从时间大的询问转移到时间小的询问,则时间转移时其实是在做一个时光倒流的操
作,也就是将修改带来的影响进行还原。像单点修改值这种操作,它的逆操作也是个单点修
改,所以实现起来与前一种情况是一样的。(为了还原,我们需要记录修改前它的值是多少)。
三维的转移分析好了之后我们发现,此时两个询问之间转移答案所需要的花费仍然是它们之间的曼哈顿距离。显然此时做一个三维曼哈顿最小生成树就能构造一个较优的路径。
但实际上我们依然可以按之前那样分块解决问题。
考虑将序列中所有点分块,块的大小为O(n ^ (2/3)) ,则共有O(n^(1/3)) 个块。将询问按左端点所在块为第一关键字、右端点所在块为第二关键字、询问的时间作为第三关键字进行排序。
回答询问时也需要多添加一个时间指针,左右端点指针仍然像之前那样移动,现在多了
一个时间指针的处理。时间指针的移动与普通指针一样,但根据修改操作的不同,具体的处
理也会不一样,时间的倒流与前进也会有些区别,这需要具体题目具体分析.
维护两个数组,一个记录修改操作,一个记录查询操作. 对于每个查询操作,记录一个时间戳,为在它之前的那一个修改操作的编号.然后将查询数组排序.
维护一个Time指针,每次先移动Time指针,如果Time<当前查询操作的时间戳,那么进行所有小于时间戳的修改操作,否则就要改回来. 然后就是普通莫队了.
#include <cstdio> #include <cmath> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 10010; int n,m,a[maxn],last[maxn],cnt1,cnt2,block,Time,L = 1,R = 0,anss[maxn],ans,cnt[maxn * 100]; struct node { int x,y,last; }md[maxn]; struct node2 { int x,y,tim,id,lpos,rpos; }q[maxn]; bool cmp(node2 a,node2 b) { if (a.lpos == b.lpos && a.rpos == b.rpos) return a.tim < b.tim; if (a.lpos == b.lpos) return a.rpos < b.rpos; return a.lpos < b.lpos; } void update(int poss,int v) { int pos = md[poss].x; if (L <= pos && pos <= R) { cnt[a[pos]]--; if (cnt[a[pos]] == 0) ans--; a[pos] = v; if (cnt[a[pos]] == 0) ans++; cnt[a[pos]]++; } else a[pos] = v; } void add(int x,int v) { int pre = cnt[a[x]]; cnt[a[x]] += v; if (pre == 0 && cnt[a[x]] == 1) ans++; else if (pre == 1 && cnt[a[x]] == 0) ans--; } void solve() { sort(q + 1,q + 1 + cnt2,cmp); for (int i = 1; i <= cnt2; i++) { int l = q[i].x,r = q[i].y,id = q[i].id; while (Time < q[i].tim) { ++Time; update(Time,md[Time].y); } while (Time > q[i].tim) { update(Time,md[Time].last); Time--; } while (L < l) add(L++,-1); while (L > l) add(--L,1); while (R < r) add(++R,1); while (R > r) add(R--,-1); anss[id] = ans; } } int main() { scanf("%d%d",&n,&m); block = pow(n,2.0/3.0); for (int i = 1; i <= n; i++) { scanf("%d",&a[i]); last[i] = a[i]; } for (int i = 1; i <= m; i++) { char ch[2]; scanf("%s",ch); if (ch[0] == 'Q') { ++cnt2; scanf("%d%d",&q[cnt2].x,&q[cnt2].y); q[cnt2].tim = cnt1; q[cnt2].lpos = (q[cnt2].x - 1) / block + 1; q[cnt2].rpos = (q[cnt2].y - 1) / block + 1; q[cnt2].id = cnt2; } else { ++cnt1; scanf("%d%d",&md[cnt1].x,&md[cnt1].y); md[cnt1].last = last[md[cnt1].x]; last[md[cnt1].x] = md[cnt1].y; } } solve(); for (int i = 1; i <= cnt2; i++) printf("%d ",anss[i]); return 0; }