数据结构 - 启发式合并

  定义:将两个数据结构合并时,应将小的数据结构中的元素一个一个分别插入大的数据结构。



顺便写了一道“简单”题 ——梦幻布丁 (可坑死我了是我太弱了)

现在回过头来这道题真的不难,我只是栽到以前挖的坑里去了(链表没学好)

这告诉我们一个道理 —— 千万不要边走边挖坑啊,有坑赶紧填!

我jio得这篇代码注释真的很良心(因为几乎没有人给这么简单的代码标注释)

好了...... 笔者叨叨叨正式结束

  1.    颜色一定是单调不增,段数一定是单调不增的,可以一边修改一边 ans--
  2. 用链表维护每一个颜色的下标,启发式合并,每次取出较小的,如果某个位置的前面或后面原来不同改颜色后相同了就 ans--

——摘自大米饼

 1 #include<cstdio>
 2 #include<iostream>
 3 #define N 100010
 4 #define M 1000010
 5 using namespace std;
 6 int n,m,ans;
 7 int c[N],next[N],ft[M],head[M],s[M],st[M];
 8 //c[i]表示第i个数字的颜色
 9 //ft[i]第i种颜色为 ft[i]
10 //head[i]第 i种颜色的表头,next[i]记下一个
11 //s[i]第 i种颜色的个数
12 //st[i]第 i种颜色的首位置
13 int read(){
14     int x=0,f=1; char c=getchar();
15     while(c<‘0‘||c>‘9‘) {if(c==‘-‘) f=-1; c=getchar();}
16     while(c>=‘0‘&&c<=‘9‘) x=x*10+c-‘0‘,c=getchar();
17     return x*f;
18 }
19 void solve(int a,int b){
20     for(int i=head[a];i;i=next[i]){     //遍历每个颜色为 a的元素
21         if(c[i+1]==b) ans--;        //更改后的颜色 与 右邻颜色相同
22         if(c[i-1]==b) ans--;        //更改后的颜色 与 左邻颜色相同
23     }
24     for(int i=head[a];i;i=next[i]) c[i]=b;    // 改变颜色 a->b  head[a]==b
25     next[st[a]]=head[b]; head[b]=head[a];     // a(内部实际颜色已改变)合并到 b上
26     s[b]+=s[a];            //b颜色的个数  累加上  a(已改变为 b)的个数
27     head[a]=st[a]=s[a]=0;        //有关 a  清零
28 }
29 int main()
30 {
31     n=read(),m=read();
32     for(int i=1;i<=n;i++){
33         c[i]=read();
34         ft[c[i]]=c[i];
35         if(c[i]!=c[i-1]) ans++;
36         if(!head[c[i]]) st[c[i]]=i;
37         s[c[i]]++; next[i]=head[c[i]]; head[c[i]]=i;
38         //这个赋值法就是这样,和邻接表一个道理(一直不太会,因为会打就一直咕着),一开始折磨死我了,还好没有放弃。
39         //我是带了一组简单的数据模拟了一遍(我太弱了),现在看多了就好多了 ,加油 ^_^
40     }
41     for(int i=1;i<=m;i++){
42         int a,b,x=read();
43         if(x==2) printf("%d\n",ans);
44         else{
45             a=read(),b=read();
46             if(a==b) continue;        //改变为相同颜色=不变
47             if(s[ft[a]]>s[ft[b]]) swap(ft[a],ft[b]);    //保证 小的(a)合并进大的(b)
48             a=ft[a],b=ft[b];    // ft[x] 用于维护实际颜色(x)
49             if(s[a]==0) continue;    // 没有 a的颜色
50             solve(a,b);
51         }
52     }
53     return 0;
54 }

原文地址:https://www.cnblogs.com/RR-Jin/p/11626077.html

时间: 2024-10-11 18:18:11

数据结构 - 启发式合并的相关文章

数据结构(trie,启发式合并):HDU 5841 Alice and Bob

这个对数字建二进制trie,然后启发式合并,最重要的是Push_up()操作和Merge()操作,解决了复杂度的问题. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int N=100010,M=2000010; 6 int cnt,fir[N],to[N*2],nxt[N*2]; 7 void addedge(int a,int

数据结构的合并

引言 有一类经典的数据结构问题,要求高效地支持以下几种操作: 1.新建一个数据结构 2.将两个数据结构的信息合并(要求合并操作满足交换律.结合律) 3.在数据结构中查询某些信息 这类问题有时需要根据数据结构的特性设计操作,同时也存在一类较为通用的解决方法. 合并操作可以看做批量地在数据结构中添加信息,而一般的插入操作则是合并的特例 定义与约定 设n表示问题中新建的数据结构个数 |A|表示数据结构A由几个数据结构合并得到 A+B表示合并A,B得到的数据结构,因此有|A+B|=|A|+|B| 直接合

BZOJ 2809 APIO2012 dispatching Treap+启发式合并 / 可并堆

题目大意:给定一棵树,选定一棵子树中的一些点,薪水和不能超过m,求点的数量*子树根节点的领导能力的最大值 考虑对于每个节点,我们维护一种数据结构,在其中贪心寻找薪金小的雇佣. 每个节点暴力重建一定不行,我们考虑可并数据结构,每个节点将子节点的信息直接合并即可 可以用启发式合并的Treap,也可以用可并堆 今天特意去学了这玩应0.0 先写了左偏树 然后又写了下随机堆-- 后者速度上更快一些 不过建议从左偏树开始学起 总之平衡树常数各种大就是了0.0 Treap+启发式合并 #include<cst

【主席树 启发式合并】bzoj3123: [Sdoi2013]森林

小细节磕磕碰碰浪费了半个多小时的时间 Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数表示 N个节点上的权值.  接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为“Q x y k”或者“L x y ”,其含义见题目描述部分. Output 对于每

HDU6191 Query on A Tree (01字典树+启发式合并)

题意: 给你一棵1e5的有根树,每个节点有点权,1e5个询问(u,x),问你子树u中与x异或最大的值是多少 思路: 自下而上启发式合并01字典树,注意合并时清空trie 线段树.字典树这种结构确定的数据结构,启发式合并的时候不需要考虑次序,复杂度都是nlogn 代码: 2200 / 10000ms , 60 / 128 M #include<iostream> #include<cstdio> #include<algorithm> #include<cmath&

模板:启发式合并

首先说明一点:线段树合并不是启发式合并. 启发式合并的大概内容就是:把小的数据结构按照这个数据结构的正常插入方法,一个一个地暴力塞进去. 而线段树合并显然不是这个东西. 这道题的题解太烂了,所以耽误了很长时间. 对于每一次操作,它只有3个参数:起始位置,作用时间,颜色. 把颜色离散化一下,让它们的编号分布在1e5以内.也可以不离散化,略麻烦一些而已.注意有负数. 现在我们维护一个以时间为区间下标的线段树,里面维护两个权值,一个是第一次出现的小球的个数,另一个是所有小球的个数. 我们另开一组vec

【Splay】【启发式合并】hdu6133 Army Formations

题意:给你一颗树,每个结点的儿子数不超过2.每个结点有一个权值,一个结点的代价被定义为将其子树中所有结点的权值放入数组排序后,每个权值乘以其下标的和.让你计算所有结点的代价. 二叉树的条件没有用到. 每个结点开一个Splay,从叶子往上启发式合并上去,可以先bfs一遍确定合并顺序.每一次将Splay大小较小的结点的权值全提取出来塞到较大的里面. 由于权值可能重复出现,所以每个结点记个cnt. 答案统计的时候,就将那个刚塞进去的旋到根,然后答案加上左子树的权值和,再加上(右子树的权值的个数+该结点

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.

学习笔记::启发式合并

昨天做Tree Rotation,没发现自己写的是暴力,还要了数据...... 然后发现好像必须得用启发式合并 不想学线段树,学了个splay的 --------------------------------------------------- 假设现在有n个点,每个点是一个splay,互不连起来 假设我们每次让两个不连通的splay联通, 所谓启发式:就是把小的合并到大的上,这样使复杂度有保证 怎么合并呢?就是先把小的splay的每个点找出来,然后插入到大的splay ----------