【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

Sample Output

3
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 }
View Code

 

posted @ 2015-03-12 19:31  TCtower  阅读(197)  评论(0编辑  收藏  举报