P3201 [HNOI2009] 梦幻布丁 将颜色x变成颜色y 问总共有多少种颜色 启发式合并+向量代替链表

https://www.luogu.com.cn/problem/P3201

题目描述

nn 个布丁摆成一行,进行 mm 次操作。每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色。

例如,颜色分别为 1,2,2,11,2,2,1 的四个布丁一共有 33 段颜色.

输入格式

第一行是两个整数,分别表示布丁个数 nn 和操作次数 mm。
第二行有 nn 个整数,第 ii 个整数表示第 ii 个布丁的颜色 a_iai
接下来 mm 行,每行描述一次操作。每行首先有一个整数 opop 表示操作类型:

  • 若 op = 1op=1,则后有两个整数 x, yx,y,表示将颜色 xx 的布丁全部变成颜色 yy。
  • 若 op = 2op=2,则表示一次询问。

输出格式

对于每次询问,输出一行一个整数表示答案。

输入输出样例

输入 #1
4 3
1 2 2 1
2
1 2 1
2
输出 #1
3
1

说明/提示

样例 1 解释

初始时布丁颜色依次为 1, 2, 2, 11,2,2,1,三段颜色分别为 [1, 1], [2, 3], [4, 4][1,1],[2,3],[4,4]。
一次操作后,布丁的颜色变为 1, 1, 1, 11,1,1,1,只有 [1, 4][1,4] 一段颜色。

数据规模与约定

对于全部的测试点,保证 1 \leq n, m \leq 10^51n,m105,1 \leq a_i ,x, y \leq 10^61ai,x,y106。

提示

请注意,不保证颜色的编号不大于 nn,也不保证 x \neq yx=y,mm 不是颜色的编号上限。

分析

n 个操作,每次将 n 个数合并,直接操作的话是 n^2 ,但是每次把数量少的 颜色 合并到数量大的颜色,启发式操作是n logn

启发式操作:if(sz[x] < sz[y]) swap(x,y);p[y] = x;

然后用链表把 颜色内的元素串起来 ,每次合并操作的时候,改变元素较少的链表的所有元素,然后把它接到元素较多的颜色上去(新概念并查集)

注意到,如果当前颜色是x,要被改成 y ,如果颜色为x 的数量比 y 的数量多,会有一个问题,之后如果想修改颜色 y ,但是由于y 已经合并到 x 上了,会找不到 y ,这里定义一个 并查集数组f,

当出现这种情况的时候就交换 f,然后让各自链表合并

if(sz[f[x]] > sz[f[y]]) swap(f[x],f[y]);

 

#include <iostream>
#include <cstdio>

const int N=1e5+5,M=1e6+5;
int n,m,c[N],sz[M],st[M],f[M],hd[M],nxt[N],ans;

//链表 
void merge(int x,int y) {
    for(int i=hd[x];i;i=nxt[i]) ans-=(c[i-1]==y)+(c[i+1]==y);
    for(int i=hd[x];i;i=nxt[i]) c[i]=y;
    nxt[st[x]]=hd[y],hd[y]=hd[x],sz[y]+=sz[x];
    hd[x]=st[x]=sz[x]=0;
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) {
        scanf("%d",&c[i]),f[c[i]]=c[i];
        ans+=c[i]!=c[i-1];
        if(!hd[c[i]]) st[c[i]]=i;
        ++sz[c[i]],nxt[i]=hd[c[i]],hd[c[i]]=i;
    }
    while(m--) {
        int opt;
        scanf("%d",&opt);
        if(opt==2) printf("%d\n",ans);
        else {
            int x,y;
            scanf("%d%d",&x,&y);
            if(x==y) continue;
            if(sz[f[x]]>sz[f[y]]) std::swap(f[x],f[y]);
            if(!sz[f[x]]) continue;
            merge(f[x],f[y]);
        }
    }
    return 0;
}

 向量代替链表:

//-------------------------代码----------------------------

//#define int ll
const int N = 2e5+10;
int n,m,ans;
V<int> v[N];
int f[N],c[N];

void merge(int x,int y) {
    for(int i:v[x]) {
        if(c[i-1] == y) ans -- ;
        if(c[i+1] == y) ans -- ;
    }
    for(auto i:v[x]){
        c[i] = y;
        v[y].pb(i);
    }
    v[x].clear();
}

void solve()
{
    fo(i,1,n) f[i] = i;
    cin>>n>>m;
    fo(i,1,n) {
        cin>>c[i];
        f[c[i]] = c[i];
        if(c[i] != c[i-1]) ans ++ ;
        v[c[i]].pb(i);
    }
    fo(i,1,m) {
        int op;cin>>op;
        if(op == 2) {
            cout<<ans<<endl;
        } else {
            int x,y;cin>>x>>y;
            if(x == y) continue;
            if(v[f[x]].size() > v[f[y]].size()) swap(f[x],f[y]);
            merge(f[x],f[y]);
        } 
    }
//    cin>>n>>m;
}
void main_init() {}
signed main(){
    AC();clapping();TLE;
    cout<<fixed<<setprecision(12);
    main_init();
//  while(cin>>n,n)
//  while(cin>>n>>m,n,m)
//    int t;cin>>t;while(t -- )
    solve();
//    {solve(); }
    return 0;
}

/*样例区


*/

//------------------------------------------------------------

 

posted @ 2022-08-25 00:51  er007  阅读(38)  评论(0编辑  收藏  举报