• [HNOI2009]梦幻布丁


    题目描述

    N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.

    输入输出格式

    输入格式: 

    第一行给出N,M表示布丁的个数和好友的操作次数. 第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,对于每个操作,若第一个数字是1表示要对颜色进行改变,其后的两个整数X,Y表示将所有颜色为X的变为Y,X可能等于Y. 若第一个数字为2表示要进行询问当前有多少段颜色,这时你应该输出一个整数. 0

    输出格式:

    针对第二类操作即询问,依次输出当前有多少段颜色.

    输入输出样例

    输入样例#1:
    4 3
    1 2 2 1
    2
    1 2 1
    2
    输出样例#1:
    3
    1

    说明

    1<=n,m<=100,000; 0<Ai,x,y<1,000,000

    题解:

    用链表+启发式合并

    1:将两个队列合并,有若干队列,总长度为n,直接合并,最坏O(N),

    2:启发式合并呢?

    每次我们把短的合并到长的上面去,O(短的长度)

    咋看之下没有多大区别,

    下面让我们看看均摊的情况:

    1:每次O(N)

    2:每次合并后,队列长度一定大于等于原来短的长度的两倍。

    这样相当于每次合并都会让短的长度扩大一倍以上,

    最多扩大logN次,所以总复杂度O(NlogN),每次O(logN)。

    这题很容易搞混,启发式合并可以这么想,把x换成y可以视为把y换成x,在当前不会对答案有影响

    但之后就会换错颜色,所以从把x换成y变为把y换成x之后还要记下当前颜色的实际颜色(启发式合并做了交换)

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 using namespace std;
     6 int nxt[1000001],head[1000001],color[1000001],set[1000001],size[1000001],ans,first[1000001],n,m;
     7 void solve(int x,int y)
     8 {int i;
     9     for (i=head[x];i;i=nxt[i])
    10     {
    11         if (y==color[i-1]) ans--;
    12         if (y==color[i+1]) ans--;
    13     }
    14     for (i=head[x];i;i=nxt[i])
    15     {
    16         color[i]=y;
    17     }
    18     nxt[first[x]]=head[y];
    19     head[y]=head[x];
    20     size[y]+=size[x];
    21     head[x]=size[x]=first[x]=0;
    22 }
    23 int main()
    24 {int i,j,opt,x,y;
    25    cin>>n>>m;
    26    for (i=1;i<=n;i++)
    27    {
    28          scanf("%d",&color[i]);
    29          if (color[i]!=color[i-1]) ans++;
    30          set[color[i]]=color[i];
    31          if (head[color[i]]==0) first[color[i]]=i;
    32          size[color[i]]++;
    33          nxt[i]=head[color[i]];
    34          head[color[i]]=i;
    35    }    
    36     for (i=1;i<=m;i++)
    37     {
    38         scanf("%d",&opt);
    39         if (opt==1)
    40         {
    41             scanf("%d%d",&x,&y);
    42             if (x==y) continue;
    43             if (size[set[x]]>size[set[y]]) swap(set[x],set[y]);
    44             x=set[x];y=set[y];
    45             if (size[x]==0) continue;
    46             solve(x,y);
    47         }
    48         else 
    49         {
    50             printf("%d
    ",ans);
    51         }
    52     }
    53 }
  • 相关阅读:
    Linux系统性能优化
    Linux内核模块
    Linux守护进程的启动方法
    已有的游戏如何快速稳定迁移到云上?
    项目重构--使用策略模式
    设计模式学习--装饰者模式(Decorator Pattern)
    C#/ASP.NET应用程序配置文件app.config/web.config的增、删、改操作
    Resharper上手指南
    ReSharper 配置及用法(转)
    使用线程新建WPF窗体(公用进度条窗体)
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/7262578.html
Copyright © 2020-2023  润新知