luogu P3201 [HNOI2009]梦幻布丁

传送门

先考虑暴力,显然每次是把一个位置集合和另一个集合合并,同时维护答案,合并的过程中如果两个集合每有一对元素相邻,答案就减1

优化暴力的话,说到合并,怎么能不想起启发式合并呢?每次把一个大小小的集合并到大的上面,如果一个元素合并一次是\(O(1)\),总复杂度就是\(O(nlogn)\).实现的化可以使用平衡树/set,也可以大力链表

注意可能本来是\(x\)变成\(y\)颜色的,但是因为启发式合并,交换\(x,y\)以后变成\(y\)\(x\),这样会出问题,这个时候要用\(f_x\)表示\(x\)指代的颜色,然后交换只要交换\(f\)救星了

更多细节详见代码

#include<bits/stdc++.h>
#define LL long long
#define db double
#define il inline
#define re register

using namespace std;
const int N=1e6+10;
il int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int n,m,c[N],ans,f[N],sz[N],nt[N],hd[N],tl[N];

int main()
{
    n=rd(),m=rd();
    for(int i=1;i<=n;++i)
    {
        c[i]=rd(),ans+=c[i]!=c[i-1];
        f[c[i]]=c[i];
        if(!tl[c[i]]) hd[c[i]]=i;
        else nt[tl[c[i]]]=i;
        ++sz[c[i]],tl[c[i]]=i;
    }
    while(m--)
    {
        int op=rd();
        if(op==1)
        {
            int &x=f[rd()],&y=f[rd()];
            if(x==y) continue;
            if(sz[x]>sz[y]) swap(x,y);
            if(!sz[x]) continue;
            for(int i=hd[x];i;i=nt[i]) ans-=(c[i-1]==y)+(c[i+1]==y);
            for(int i=hd[x];i;i=nt[i]) c[i]=y;
            nt[tl[x]]=hd[y],hd[y]=hd[x],sz[y]+=sz[x],x=sz[x]=hd[x]=tl[x]=0;
        }
        else printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/smyjr/p/10410349.html