【BZOJ1483】【链表启发式合并】梦幻布丁
Description
N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.
Input
第一行给出N,M表示布丁的个数和好友的操作次数. 第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,对于每个操作,若第一个数字是1表示要对颜色进行改变,其后的两个整数X,Y表示将所有颜色为X的变为Y,X可能等于Y. 若第一个数字为2表示要进行询问当前有多少段颜色,这时你应该输出一个整数. 0
Output
针对第二类操作即询问,依次输出当前有多少段颜色.
Sample Input
4 3
1 2 2 1
2
1 2 1
2
1 2 2 1
2
1 2 1
2
Sample Output
3
1
1
HINT
Source
【分析】
转来的启发式合并的复杂度均摊分析:orzz
每次我们把短的合并到长的上面去,O(短的长度)
咋看之下没有多大区别,
下面让我们看看均摊的情况:
1:每次O(N)
2:每次合并后,队列长度一定大于等于原来短的长度的两倍。
这样相当于每次合并都会让短的长度扩大一倍以上,
最多扩大logN次,所以总复杂度O(NlogN),每次O(logN)。
就是裸题了,搞一个链表把每种颜色段的开头位置记录下来,然后每次修改暴力修改+启发式合并就行。
还有就是记得把因为大小而导致错误的颜色用一个数组映射。
1 /* 2 纳兰性德 3 人生若只如初见,何事秋风悲画扇。 4 等闲变却故人心,却道故人心易变。 5 骊山语罢清宵半,泪雨霖铃终不怨。 6 何如薄幸锦衣郎,比翼连枝当日愿。 7 */ 8 #include <iostream> 9 #include <cstdio> 10 #include <algorithm> 11 #include <cstring> 12 #include <vector> 13 #include <utility> 14 #include <iomanip> 15 #include <string> 16 #include <cmath> 17 #include <queue> 18 #include <assert.h> 19 #include <map> 20 #include <ctime> 21 #include <cstdlib> 22 #include <stack> 23 #define LOCAL 24 const int MAXN = 100000 + 10; 25 const int INF = 100000000; 26 const int SIZE = 450; 27 const int MAXM = 1000000 + 10; 28 const int maxnode = 0x7fffffff + 10; 29 using namespace std; 30 struct Node{ 31 int num; 32 Node *next; 33 }*head[MAXM];//head为表头 34 int cnt[MAXM], rem[MAXM], data[MAXM]; 35 int tot, n, m; 36 37 //在链表中加入颜色为x的节点 38 void add(int x, int d){//d代表位置 39 if (cnt[x] == 1){ 40 head[x] = new Node; 41 head[x]->num = d; 42 head[x]->next = NULL; 43 }else{ 44 //在表头插入 45 Node *p = new Node; 46 p->num = d; 47 p->next = head[x]; 48 head[x] = p; 49 } 50 } 51 void init(){ 52 tot = 0;//记录颜色的总数 53 memset(cnt, 0, sizeof(cnt));//记录颜色的数量 54 scanf("%d%d", &n, &m); 55 //for (int i = 1; i <= n; i++) rem[i] = i; 56 data[0] = -INF; 57 for (int i = 1; i <= n; i++){ 58 scanf("%d", &data[i]);//输入颜色 59 if (data[i] != data[i - 1]) tot++; 60 cnt[data[i]]++; 61 add(data[i], i); 62 rem[data[i]] = data[i];//防错数组初始化 63 } 64 65 } 66 //将a颜色变成b颜色 67 void change(int a, int b){ 68 //不要搞错了是比较正确颜色的个数 69 if (cnt[rem[a]] > cnt[rem[b]]) swap(rem[a], rem[b]); 70 a = rem[a];//总是让颜色数量少的变成多的 71 b = rem[b]; 72 if (cnt[a] == 0) return; 73 cnt[b] += cnt[a]; 74 cnt[a] = 0; 75 Node *cur; 76 for (cur = head[a]; cur != NULL; cur = cur->next){ 77 if (data[cur->num + 1] == b) tot--; 78 if (data[cur->num - 1] == b) tot--; 79 } 80 for (cur = head[a]; cur->next != NULL; cur = cur->next) data[cur->num] = b; 81 data[cur->num] = b; 82 //最后将a插在b后面 83 cur->next = head[b]; 84 head[b] = head[a]; 85 } 86 void work(){ 87 for (int i = 1; i <= m; i++){ 88 int t, a, b; 89 scanf("%d", &t); 90 if (t == 2) printf("%d\n", tot); 91 else{ 92 scanf("%d%d", &a, &b); 93 if (a == b) continue;//两种颜色相同 94 change(a, b); 95 } 96 } 97 //for (int i = 1; i <= n; i++) printf("%d", data[i]); 98 } 99 100 int main(){ 101 102 init(); 103 work(); 104 return 0; 105 }