启发式合并

T1 梦幻布丁

学习玄学优化,启发式合并(还是为了做大根堆)

//梦幻布丁
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int mx=1e6+1000;
//数组开小了 
int a[mx],head[mx],nex[mx];
int st[mx];
int sz[mx];
int n,m,len;
int col[mx];
int ans;
struct Node{
    int to;
    int next;
}e[mx*3];
//当前一共有多少段颜色,而不是多少种
//也就是说必须去把每个都更新 
void merge(int x,int y){
    for(int i=head[x];i;i=nex[i]){
        if(a[i-1]==y)ans--;
        if(a[i+1]==y)ans--;
    }
    for(int i=head[x];i;i=nex[i]){
        a[i]=y;//暴力更改每一个x 
    }
    nex[st[x]]=head[y];//把x整体接到y后面
    head[y]=head[x];
    sz[y]+=sz[x];
    head[x]=sz[x]=st[x]=0; 
}
void Solve(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
        col[a[i]]=a[i];
        if(a[i]!=a[i-1])ans++;
        if(head[a[i]]==0)st[a[i]]=i;
        sz[a[i]]++;
        nex[i]=head[a[i]];
        head[a[i]]=i;//依据i建了个链表,比前向星还省事 
        
    }
    //把每种颜色建一个集合,每次更换颜色就并查集?
    //但这样查询就很麻烦 
    for(int i=1;i<=m;++i){
        int q;
        scanf("%d",&q);
        if(q==2)printf("%d\n",ans);
        else {
            int x,y;
            scanf("%d%d",&x,&y);
            //我要把x颜色换成y,但是如果x颜色的个数大于y
            //那我就把y颜色全都换成x,但是名义上还是换成了y
            //col[x]是x,换成y还是x,但是如果换,把col[x]改成y,col[y]改成x
            //这样当我们要把y换成z,y实际上让我们换成x,那就去找col[y]=x;
            //当要把z换成x,肯定不能去换x,因为那里存着x和y,去换y,此时那里为空
            //就相当于x.所以用一个swap 
            
            
            //启发式合并:就这个题,你看啊,你要是莽着直接换,每次暴力去更新x的每一个数 
            //极限是(n-1)*m没错吧,每个元素(-1)都被更改m次
            //但是如果你每次去把那个小的去改成大的,那么在改完以后
            //的集合里,这个个数肯定是原来那个小的两倍以上吧,每次更新都最多只会去更新
            //n/2个点,这也不对,当你换了n/2个点,下次换必定就是1或零了
            // 每个元素至多被更改log n次,这么想,每次更新,一个我们更改的集合总数就要*2往上
            //乘log(n)次后面就是零常更改了, 但是显然,后面肯定不用大的改
            //我说这么多废话意思就是 网上都说复杂度是 n*log(n) 但我觉得实际比这个可能还要小很多
            // n*m-->n*log(n) 就是这么玄学 
            
            if(x==y)continue;
            if(sz[col[x]]>sz[col[y]])swap(col[x],col[y]);
            if(sz[col[x]]==0)continue;
            merge(col[x],col[y]);
        } 
    }
}
int main(){
    Solve();
    return 0;
}
 
View Code

 

posted @ 2022-04-15 15:01  SMTwy  阅读(39)  评论(0编辑  收藏  举报