[BZOJ1483][HNOI2009]梦幻布丁(链表+启发式合并)

211 篇文章 0 订阅
5 篇文章 0 订阅

题目描述

传送门

题解

将同一种颜色建成一个链表,然后每次合并的时候将小的遍历一遍看一看两边的是否和它是同一个颜色,动态维护答案,然后将小的直接接到大的后面就可以了。这样的话因为每次都是将小的合并到大的里面去,那么每次至少放大到两倍,那么对于每一个点来说最多被合并logn次,均摊复杂度 O(nlogn)
不过这里有一个坑点:每一次只能将小的遍历一遍,也就是说只能修改小的的颜色。但是题目不一定要修改小的的颜色,我刚开始只是保证了两个链表是一个颜色的,但是往后查询的时候就乱套了。所以要加一个数组f[i],类似于i的代表元素,也就是说颜色i相当于f[i]这种颜色。当因为链表的大小要交换的时也要将f[x]和f[y]交换。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 100005
#define C 1000005

int n,m,Max,opt,x,y,color,ans;
int a[N],nxt[N],head[C],tail[C],cnt[C],f[C];

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]); Max=max(Max,a[i]);
        if (a[i]!=a[i-1]) ans++;
        if (tail[a[i]]==0) head[a[i]]=i;
        nxt[i]=tail[a[i]];
        tail[a[i]]=i;
        cnt[a[i]]++;
    }
    for (int i=1;i<=Max;++i) f[i]=i;
    for (int i=1;i<=m;++i)
    {
        scanf("%d",&opt);
        if (opt==1)
        {
            scanf("%d%d",&x,&y);
            if (f[x]==f[y]) continue;
            if (cnt[f[x]]>cnt[f[y]]) swap(f[x],f[y]);
            x=f[x],y=f[y];
            if (cnt[x]==0) continue;
            color=y;

            for (int now=tail[x];now;now=nxt[now])
            {
                if (a[now-1]==color) ans--;
                if (a[now+1]==color) ans--;
            }
            for (int now=tail[x];now;now=nxt[now]) a[now]=color;

            nxt[head[x]]=tail[y],tail[y]=tail[x],cnt[y]+=cnt[x];
            head[x]=tail[x]=cnt[x]=0;
        }
        else printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值