bzoj1483: [HNOI2009]梦幻布丁(链表+启发式合并)
Posted Sakits
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj1483: [HNOI2009]梦幻布丁(链表+启发式合并)相关的知识,希望对你有一定的参考价值。
题目大意:一个序列,两种操作。
①把其中的一种数修改成另一种数
②询问有多少段不同的数如1 2 2 1为3段(1 / 2 2 / 1)。
昨晚的BC的C题和这题很类似,于是现学现写居然过了十分开心。
先把初始序列的答案统计出来,然后把每种数都用一个链表串起来,修改的时候把两种数的链表合并一下。修改答案的时候,比如把数x全部修改为数y,那么把数x的链表遍历一次,某个数x左边有y就把答案-1,右边有y也-1。
接下来是重点了:要把链表长度小的接在链表长度大的后面,才能做到nlogn。因为把链表长度小的接在大的后面,新链表长度一定>=原长度小的链表的长度的两倍,最多变长logn次,所以均摊下来每次修改效率O(logn),总复杂度为O(nlogn)。
代码如下:
#include<iostream> #include<cstring> #include<cstdio> #define maxn 1000005 using namespace std; int a[maxn],size[maxn],next[maxn],last[maxn],pos[maxn],f[maxn],ans,n,m; int main() { scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); if(a[i]!=a[i-1])ans++; pos[a[i]]=a[i]; if(!f[a[i]])f[a[i]]=i; size[a[i]]++;next[i]=last[a[i]];last[a[i]]=i; } for(int i=1;i<=m;i++) { int opt; scanf("%d",&opt); if(opt==1) { int x,y; scanf("%d %d",&x,&y); if(x==y)continue; if(size[pos[x]]>size[pos[y]])swap(pos[x],pos[y]); x=pos[x];y=pos[y]; if(!size[x])continue; for(int i=last[x];i;i=next[i]) { if(a[i+1]==y)ans--; if(a[i-1]==y)ans--; } for(int i=last[x];i;i=next[i])a[i]=y; size[y]+=size[x];size[x]=0; next[f[x]]=last[y];last[y]=last[x];last[x]=f[x]=0; }else printf("%d\\n",ans); } }
以上是关于bzoj1483: [HNOI2009]梦幻布丁(链表+启发式合并)的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ1483[HNOI2009]梦幻布丁 链表+启发式合并