本文主要是介绍AcWing 2154. 梦幻布丁,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
#include<bits/stdc++.h>using namespace std;//N 表示的是有多少个布丁
//M 表示的是有多少种颜色
const int N=1e5+10,M=1e6+10;//h e ne idx 是链表的组成元素
//表示的是有 M 个颜色,每一个颜色下面连着一个单链表
int h[M],e[N],ne[N],idx;
//n 表示元素个数,m 表示操作次数,ans 表示连续的
//不同颜色的段数
int n,m,ans;
//color 用来存每一个布丁的颜色
//p 用来存映射关系,意思是说,颜色对应的本来是该颜色
//本身,但是由于特殊需要,需要把颜色映射为另一个颜色
//特殊需要是指,我们需要把数目少的颜色的集合合并到
//数目多的颜色的集合中去,但是有可能需要变成的颜色的
//数目是少的集合,此时需要修改映射
//sz 表示的是,颜色的出现次数
int color[N],p[M],sz[M];//单链表的添加操作,表示的是
//把一个数字添加到该颜色往下的一个单链表中
//添加的时候顺便把颜色出现的次数更新
void add(int a,int b)
{e[idx]=b,ne[idx]=h[a],h[a]=idx++;sz[a]++;
}//合并两个集合,因为每一次合并都是把小的集合
//合并到大的集合中去,假设小的集合的大小是 x
//合并完成之后,集合的大小至少是 2x
//从而可以把 O(n) 的时间复杂度降到 O(logn)
//需要进行修改操作,需要需要加上引用符号
void merge(int& x,int& y)
{//被操作元素的颜色和需要变成的颜色相等,不需要进行操作if(x==y)return;//被操作颜色的大小更大,需要把两个颜色进行交换//这里交换的是两个映射的数值//比如说原来的 p[x]=1,p[y]=2//sz[x]=10 sz[y]=1//交换之后,p[x]=2,p[y]=1//注意后面的 x,y 表示的都是 p[x] p[y]//交换两个元素,表示把颜色出现次数少的集合//添加到颜色出现次数多的集合后面if(sz[x]>sz[y])swap(x,y);//遍历 x 所在的单链表//~i 等价于 i!=-1//因为 ~(-1)=0for(int i=h[x];~i;i=ne[i]){int j=e[i];//更新答案,假设修改之后的颜色和前一段的颜色相等//答案减小 1 ,假设和后一段颜色相等,答案也减小 1//下面是一种比较简短的写法ans-=(color[j-1]==y)+(color[j+1]==y);}//遍历 x 所在的单链表for(int i=h[x];~i;i=ne[i]){//更新颜色int j=e[i];color[j]=y;//把尾结点指向另一个单链表的头节点if(ne[i]==-1){ne[i]=h[y];//另一个单链表表示的是元素更多的集合//假设不是,前面已经交换过了,所以一定是//元素多的集合的头节点指向当前链表的头节点h[y]=h[x];//把当前链表的头节点初始化为 -1//表示空h[x]=-1;//结束循环break;}}//更新集合的大小sz[y]+=sz[x];//清空小的集合sz[x]=0;
}int main()
{//初始化头节点memset(h,-1,sizeof h);cin>>n>>m;for(int i=1;i<=n;i++){cin>>color[i];//初始化答案if(color[i]!=color[i-1])ans++;//把点添加到单链表中,相当于一条单链表上面挂了//很多单链表add(color[i],i);}//初始化映射,开始是什么颜色就对应什么映射//映射的作用是支持修改for(int i=0;i<M;i++)p[i]=i;while(m--){int op;cin>>op;//输出答案if(op==2)cout<<ans<<endl;else{// x 表示待修改的颜色//y 表示修改之后的颜色int x,y;cin>>x>>y;//p[x] p[y] 表示的是映射的颜色//p 其实是取指针的意思,表示指向的颜色merge(p[x],p[y]);}}return 0;
}
启发式合并
表示的意思是,把元素少的集合合并到元素多的集合中,这样可以使得时间复杂度降低到 log
该题最难的地方在于,假设我们要把一个元素多的颜色修改为一个元素少的颜色,怎么实现上面的要求
解决方案是弄一个指针数组,或者说映射数组,把颜色交换,保证每一次操作都是把出现次数少的颜色修改为出现次数多的颜色
修改头结点和尾结点从而实现合并,等号前面的结点表示从该节点出发,等号右边的结点表示该点是终点,
这篇关于AcWing 2154. 梦幻布丁的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!