BZOJ1483:[HNOI2009]梦幻布丁 链表启发式合并
思路:
保存所有颜色组成的链表,改颜色时就把两个链表合并,合并方式利用归并排序即可.
易知链表的数目只会越来越少,因此我们进行启发式合并,每一次将小的链表合并到大的链表里面.
这样最坏情况下只需合并\(O(logn)\)次,每一次合并复杂度为\(O(n)\),因此总的时间复杂度为\(O(nlogn)\).
#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
struct Node{
Node*nxt;
int ins;
Node(){}
Node(int _ins):ins(_ins){}
}*null;
Node*begin[N],*end[N];int c[N],siz[N];
map<int,int>M;int id;
int Ask(int x){return M[x];}
int res;
inline void Merge(int x,int y,int to){//Merge color y to color x
static Node*stk1[N],*stk2[N];
int id1=0,id2=0;
int labx=M[x],laby=M[y],lastans=0,nowans=0;
for(Node*p=begin[labx];p!=null;p=p->nxt)stk1[++id1]=p,lastans+=p->nxt->ins-p->ins>1;
for(Node*p=begin[laby];p!=null;p=p->nxt)stk2[++id2]=p,lastans+=p->nxt->ins-p->ins>1;
int i=1,j=1;
Node*last=new Node(0),*S=last;
for(;i<=id1&&j<=id2;last=last->nxt)
if(stk1[i]->ins<stk2[j]->ins)last->nxt=stk1[i++];else last->nxt=stk2[j++];
while(i<=id1)last->nxt=stk1[i++],last=last->nxt;
while(j<=id2)last->nxt=stk2[j++],last=last->nxt;
last->nxt=null;
for(Node*p=S->nxt;p!=null;p=p->nxt)nowans+=p->nxt->ins-p->ins>1;
begin[labx]=S->nxt;
siz[labx]+=siz[laby];
int t=M[x];M[x]=M[y]=0,M[to]=t;
res=res-lastans+nowans;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("tt.in","r",stdin);
#endif
int n,m;scanf("%d%d",&n,&m);
register int i,j;int x;
null=new Node(INF);
int now;
for(i=1;i<=n;++i){
scanf("%d",&x);
if(!M[x])M[x]=++id;++siz[now=M[x]];
Node*p=new Node(i);
if(end[now])end[now]->nxt=p,end[now]=p;
else begin[now]=end[now]=p;
}
for(i=1;i<=id;++i)end[i]->nxt=null;
for(i=1;i<=id;++i)
for(Node*p=begin[i];p!=null;p=p->nxt)res+=p->nxt->ins-p->ins>1;
int qte,a,b;
while(m--){
scanf("%d",&qte);
if(qte==1){
scanf("%d%d",&a,&b);
if(a==b)continue;
if(M[a]==0)continue;
if(!M[b]){M[b]=M[a],M[a]=0;continue;}
if(siz[M[a]]>siz[M[b]])Merge(a,b,b);else Merge(b,a,b);
}
else printf("%d\n",res);
}
return 0;
}
评论 (0)