• 【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
    ", 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
  • 相关阅读:
    八进制转换成十进制(你会明白的,呵呵)
    从键盘读取7个数(150)的整数值,每读一个值打印出该值个数的*号.
    两个字符串的连接程序
    判断一个素数能被几个9整除.
    809*??=800*??+9*??+1 其中??代表的两位数,8*??的结果为两位数,9*??的结果为3位数。求??代表的两位数,及809*??后的结果。
    一个偶数总能表示为两个素数的和.
    07所能组成的奇数的个数
    asp.net .ashx文件使用Server.MapPath解决方法
    MVC常见问题小总结
    amcharts_2.6.13左上角的广告咱去掉
  • 原文地址:https://www.cnblogs.com/hoskey/p/4333312.html
Copyright © 2020-2023  润新知