【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

 

你可能感兴趣的:(ZOJ)