Luogu3201 梦幻布丁 - 启发式合并 -
题目链接:https://www.luogu.com.cn/problem/P3201
题解:
考虑启发式合并,即每次把数量小的颜色块合并到大的颜色块上
对于每个颜色块,我们可以使用链表维护,具体写法可以采用前向星。(当前位置的nxt置为head[x], head[x]=当前位置),这里head[p]=i,p表示颜色,i表示位置
这样会产生一个问题:如果题目要求的是把数量大颜色的合并到小的颜色,这样颜色就是反的?
我们可以维护一个颜色数组表示颜色块当前的真实颜色,如果出现上述情况,就swap一下二者的真实颜色,并把y全部标记到x上,这样查询真实颜色的时候查到的就是就是互换前的了
改变颜色时就暴力修改,由于每次小的颜色块大小至少*2,因此最多修改log次,时间复杂度
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1e6+5;
int n,m;
int st[maxn], a[maxn], nxt[maxn],head[maxn],sz[maxn],f[maxn];
int ans = 1;
void merge(int x,int y){ // 把 x 颜色块均修改为 y 颜色块 (x y 不代表真实颜色)
for(int i=head[x];i;i=nxt[i]){
if(a[i+1] == y)-- ans;
if(a[i-1] == y)-- ans;
}
for(int i=head[x];i;i=nxt[i])a[i] = y;
nxt[st[x]] = head[y];head[y] = head[x];sz[y] += sz[x];
st[x] = 0; sz[x] = 0; head[x] = 0;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=2;i<=n;i++)if(a[i] != a[i-1])++ ans;
for(int i=1;i<=n;i++){
if(!head[a[i]])st[a[i]] = i;
nxt[i] = head[a[i]]; head[a[i]] = i;
f[a[i]] = a[i];
++ sz[a[i]];
}
while(m --){
int op;scanf("%d",&op);
if(op == 2)printf("%d\n",ans);
else{
int x,y;scanf("%d%d",&x,&y);
if(sz[f[x]] > sz[f[y]])swap(f[x], f[y]);
if(x == y || !sz[f[x]])continue;
merge(f[x], f[y]);
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示