梦幻布丁 启发式合并

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

题目描述

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

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

输入格式

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

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

输出格式

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

启发式合并

好像就是把集合的合并 变成链表插入 链表只要修改指针就可以

开一个P数组 维护每个颜色对应的集合链表 每次把小的集合插入大的集合 然后修改颜色和P的对应关系

1.单链表

h-头指针 sz-链表大小 ne-next w-该节点对应颜色列表下标

int h[N],ne[N],w[N],idx=0;//单链表
int p[N],sz[N],color[N];
ll ans=0;
void add(int c,int p){
	w[idx]=p;
	ne[idx]=h[c];
	h[c]=idx++;
	sz[c]++;
}

2.merge

对于不同的颜色的集合,进行合并操作,

先遍历,修改颜色,然后直接把这个集合(链表)挂在另一个集合(链表)头/尾

//不知道为什么1e5大小一直报错就全写1e6了,懒得看zzz

代码

#include
using namespace std;
typedef long long ll;
const int N=1e6+10;
int n,m;
int h[N],ne[N],w[N],idx=0;//单链表
int p[N],sz[N],color[N];
ll ans=0;
void add(int c,int p){
	w[idx]=p;
	ne[idx]=h[c];
	h[c]=idx++;
	sz[c]++;
}
void merge(int &x,int &y){
	if(x==y)return;
	if(sz[x]>sz[y])swap(x,y);
	for(int i=h[x];i!=-1;i=ne[i]){
		int j=w[i];
		ans-=(color[j-1]==y)+(color[j+1]==y);
	}
	for(int i=h[x];i!=-1;i=ne[i]){
		int j=w[i];
		color[j]=y;
		if(ne[i]==-1){
			ne[i]=h[y],h[y]=h[x];
			break;
		}
	}
	h[x]=-1;
	sz[y]+=sz[x],sz[x]=0;
}

int main(){
	scanf("%d%d",&n,&m);
	memset(h,-1,sizeof(h));
	for(int i=0;i

 

你可能感兴趣的:(梦幻布丁 启发式合并)