首页 > 其他分享 >P3201 [HNOI2009] 梦幻布丁 将颜色x变成颜色y 问总共有多少种颜色 启发式合并+链表

P3201 [HNOI2009] 梦幻布丁 将颜色x变成颜色y 问总共有多少种颜色 启发式合并+链表

时间:2022-08-25 01:00:24浏览次数:54  
标签:sz 颜色 int 布丁 链表 hd

https://www.luogu.com.cn/problem/P3201

题目描述

nn 个布丁摆成一行,进行 mm 次操作。每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色。

例如,颜色分别为 1,2,2,11,2,2,1 的四个布丁一共有 33 段颜色.

输入格式

第一行是两个整数,分别表示布丁个数 nn 和操作次数 mm。
第二行有 nn 个整数,第 ii 个整数表示第 ii 个布丁的颜色 a_iai​。
接下来 mm 行,每行描述一次操作。每行首先有一个整数 opop 表示操作类型:

  • 若 op = 1op=1,则后有两个整数 x, yx,y,表示将颜色 xx 的布丁全部变成颜色 yy。
  • 若 op = 2op=2,则表示一次询问。

输出格式

对于每次询问,输出一行一个整数表示答案。

输入输出样例

输入 #1
4 3
1 2 2 1
2
1 2 1
2
输出 #1
3
1

说明/提示

样例 1 解释

初始时布丁颜色依次为 1, 2, 2, 11,2,2,1,三段颜色分别为 [1, 1], [2, 3], [4, 4][1,1],[2,3],[4,4]。
一次操作后,布丁的颜色变为 1, 1, 1, 11,1,1,1,只有 [1, 4][1,4] 一段颜色。

数据规模与约定

对于全部的测试点,保证 1 \leq n, m \leq 10^51≤n,m≤105,1 \leq a_i ,x, y \leq 10^61≤ai​,x,y≤106。

提示

请注意,不保证颜色的编号不大于 nn,也不保证 x \neq yx=y,mm 不是颜色的编号上限。

分析

n 个操作,每次将 n 个数合并,直接操作的话是 n^2 ,但是每次把数量少的 颜色 合并到数量大的颜色,启发式操作是n logn

启发式操作:if(sz[x] < sz[y]) swap(x,y);p[y] = x;

然后用链表把 颜色内的元素串起来 ,每次合并操作的时候,改变元素较少的链表的所有元素,然后把它接到元素较多的颜色上去(新概念并查集)

注意到,如果当前颜色是x,要被改成 y ,如果颜色为x 的数量比 y 的数量多,会有一个问题,之后如果想修改颜色 y ,但是由于y 已经合并到 x 上了,会找不到 y ,这里定义一个 并查集数组f,

当出现这种情况的时候就交换 f,然后让各自链表合并

if(sz[f[x]] > sz[f[y]]) swap(f[x],f[y]);

 

#include <iostream>
#include <cstdio>

const int N=1e5+5,M=1e6+5;
int n,m,c[N],sz[M],st[M],f[M],hd[M],nxt[N],ans;

//链表 
void merge(int x,int y) {
    for(int i=hd[x];i;i=nxt[i]) ans-=(c[i-1]==y)+(c[i+1]==y);
    for(int i=hd[x];i;i=nxt[i]) c[i]=y;
    nxt[st[x]]=hd[y],hd[y]=hd[x],sz[y]+=sz[x];
    hd[x]=st[x]=sz[x]=0;
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) {
        scanf("%d",&c[i]),f[c[i]]=c[i];
        ans+=c[i]!=c[i-1];
        if(!hd[c[i]]) st[c[i]]=i;
        ++sz[c[i]],nxt[i]=hd[c[i]],hd[c[i]]=i;
    }
    while(m--) {
        int opt;
        scanf("%d",&opt);
        if(opt==2) printf("%d\n",ans);
        else {
            int x,y;
            scanf("%d%d",&x,&y);
            if(x==y) continue;
            if(sz[f[x]]>sz[f[y]]) std::swap(f[x],f[y]);
            if(!sz[f[x]]) continue;
            merge(f[x],f[y]);
        }
    }
    return 0;
}

 

标签:sz,颜色,int,布丁,链表,hd
From: https://www.cnblogs.com/er007/p/16622870.html

相关文章

  • 什么是双向链表?双向链表的操作封装实现(增删改查)?
    什么是双向链表?双向链表既可以从头遍历到尾,又可以从尾遍历到头也就是链表相连的过程是双向的.那么它的实现原理,你能猜到吗?一个节点既有向前连接的引用,也......
  • 合并两条有序链表
     用JavaScript实现:constlink1=newLinkedList()link1.append(1)link1.append(3)link1.append(4)link1.append(6)li......
  • 双链表和循环链表
    一、结构体定义1.双链表typedefstructDLNode{ intdata; structDLNode*prior,*next;}DLNode;2.循环链表//同双链表二、操作1.尾插法建立双链表voidcreat......
  • LeetCode 重排链表算法题解 All In One
    LeetCode重排链表算法题解AllInOnejs/ts实现重排链表重排链表原理图解//快慢指针重排链表https://leetcode.com/problems/reorder-list/https://le......
  • LeetCode 21 合并两个有序链表
    /***Definitionforsingly-linkedlist.*structListNode{*intval;*ListNode*next;*ListNode():val(0),next(nullptr){}*ListN......
  • 洛谷 P1162 填涂颜色
    题目链接:https://www.luogu.com.cn/problem/P1162试题分析:本题运用广搜,我们大体思路是这样的:首先,我们将起始位置放到队尾,然后,在队列不为空的情况下,我们要一直取队首并拓......
  • 填涂颜色
    填涂颜色思路:建立数组xx和数组yy,分别表示每一次操作横纵坐标的对应长度。将输入的方阵外面加上一圈0,第一个入队.然后从涂色的最开始(队首)向周围扩展,若扩展到的点......
  • 2.填涂颜色
    题目链接(码学堂)题目链接(洛谷)分析:这是一个简单的单一连通块问题对于这种分类明显的题,我们可以通过分类来界定一部分是  连通块以外0 vis[i][j]=1; 一部分是  ......
  • 填涂颜色
    如果左上角位置是0的话,我们可以轻松将所有在闭合曲线外的0标记。为防止出现在左上角堆了一堆1的情况,可以在图像的周围包一层0,原来图像中暴露到边界的0肯定和这外围的0联通......
  • LeetCode 142. 环形链表 II
    思路:快慢指针法:当快指针与慢指针相遇时,分别从起点,相遇点开始走,相遇即为环入口/***Definitionforsingly-linkedlist.*structListNode{*intval;*......