bzoj1483: [HNOI2009]梦幻布丁

启发式合并。

启发式合并就是每次将小的合并进大的里面。每次合并复杂度为O(n),因为每回大小都会翻倍,所以总复杂度就是O(nlogn)。

首先用链表维护每一种颜色。

询问直接输出答案。

否则合并(要记住,如果俩个其中一个是空的,直接特判,否则会浪费时间导致tle)。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 100000 + 10;
const int maxm = 1000000 + 10;
const int INF = 0x3f3f3f3f;

int size[maxm],g[maxm],last[maxm];
int next[maxn],a[maxn];
int n,m,x,y,cur,ans,op,end,r,tmp,tmp2;

void build() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        size[a[i]]++;
        if(!g[a[i]]) g[a[i]]=i;
        else next[last[a[i]]]=i;
        last[a[i]]=i;
        if(a[i]!=a[i-1]) ans++;
    }
}

void merge(int x,int y) {
    size[y]+=size[x];
    size[x]=0;
    next[0]=g[y]; 
    next[last[y]]=INF; cur=g[x];
    next[last[x]]=INF;
    for(int i=0;i<maxn;i=max(next[i],tmp2)) {
        tmp2=next[i];
        if(cur<next[i]) {
            if(cur==i+1 && i) ans--;
            for(r=cur;next[r]<next[i]&&r!=last[x];r=next[r]);
            if(r+1==next[i]) ans--;
            tmp=next[r];
            next[r]=next[i];
            next[i]=cur;
            cur=tmp;     
        }
    }
    if(r>last[y]) last[y]=r;
    g[y]=min(g[y],g[x]);
}


void change(int x,int y) {
    g[y]=g[x];
    last[y]=last[x];
    size[y]=size[x];
    g[x]=last[x]=size[x]=0;
}

void solve() {
    while(m--) {
        scanf("%d",&op);
        if(op==2) printf("%d
",ans);
        else {
            scanf("%d%d",&x,&y);
            if(size[x]==0 || x==y) continue;
            else if(size[y]==0) change(x,y);
            else if(size[x]<=size[y]) 
                    merge(x,y);
                else {
                    merge(y,x);
                    change(x,y);
                }
        }
    }
}

int main() {
    build();
    solve();
    return 0;
}
原文地址:https://www.cnblogs.com/invoid/p/5538120.html