启发式合并

启发式合并:它的本质很简单:每次把短的合并到长的上面,那么合并一次的复杂度为 O(|短的队列|)O(∣短的队列∣)

就是一种思想 有n个集合, 我们每次进行合并操作 ,总是希望把小的合并到大的 ,这样得到的集合一定是 原来集合的2倍 所以 每个元素被复杂度是logn的 ,有n个元素 所以总的时间复杂度是onlogn 。

魔板 布丁

上面是一个板子题

题目描述

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

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

输入格式

第一行是两个整数,分别表示布丁个数 n 和操作次数 m。
第二行有 n 个整数,第 ii 个整数表示第 i个布丁的颜色 a
接下来 m 行,每行描述一次操作。每行首先有一个整数 op 表示操作类型:

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

输出格式

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

我们的做法是启发式合并 ,个人理解,就是 他并不在乎颜色,他只在乎段数 ,我们可以开一个pos数组 记录每个颜色他所在的下标,然后进行合并操作的时候,判断两个颜色size的 大小 如果大的容进小的,我们可以互换 pos ,然后进行操作,,如果是空的 我们直接continue, 写个modify函数。 把ans 记录一下, 

ans-= (a[p]!=a[p-1]) + (a[p]!=a[p+1]);
				a[p] = col;
				ans += (a[p] != a[p-1])+(a[p]!=a[p+1]);

对应的a 就是我们原来记录的数组, 这里的p 就是从pos[x]拿出来得到的位置,并对对应的位置修改。 很后面代码详细看下。 也就是说 我们修改a数组是 ,从小到大合并,但实际并不是这个 ,我们实际的对应在pos中体现  在修改后,我们把pos[x] 给清除。,  下面贴出ac代码

# include<bits/stdc++.h>

using namespace std;
typedef long long LL;

const int N = 101000;
const int M = 1010000;
vector<int>pos[M];
int n,m,a[N],ans;
int main(){
	scanf("%d%d",&n,&m);
	for(int i = 1 ;i<=n;i++){
		scanf("%d",&a[i]);
		pos[a[i]].push_back(i);  
	}
	for(int i = 1 ;i<=n+1;i++){
		ans += a[i] != a[i-1];
	}
	for (int i =0;i<m;i++){
		int op;
		scanf("%d",&op);
		if ( op == 2){
			printf("%d\n",ans-1);
		}else{
			int x ,y;
			scanf("%d%d",&x,&y);
			if(x == y) continue;
			if(pos[x].size() > pos[y].size()){
				pos[x].swap(pos[y]);
			}
			if(pos[y].empty()) continue;
			auto modify = [&](int p ,int col){
				ans-= (a[p]!=a[p-1]) + (a[p]!=a[p+1]);
				a[p] = col;
				ans += (a[p] != a[p-1])+(a[p]!=a[p+1]);
			};
			int col = a[pos[y][0]];
			for (int p : pos[x]){
				modify(p,col);
				pos[y].push_back(p);
			}
			pos[x].clear();
			// 很巧妙 a 全部变成了 2 2 2 2 2 虽然要把2 变成1 但其实变得是1 到2  但是pos 数组记录的是实际操作的。。 
		}
	}
	
	
	return 0;
} 

后记 :感觉自己表达不太好,写题解真的需要下很多功夫。。orz  (给大佬跪了 %%%

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值