P3201 [HNOI2009] 梦幻布丁——线段树合并 set启发式合并

[HNOI2009] 梦幻布丁

题目描述

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

例如,颜色分别为 \(1,2,2,1\) 的四个布丁一共有 \(3\) 段颜色.

输入格式

第一行是两个整数,分别表示布丁个数 \(n\) 和操作次数 \(m\)
第二行有 \(n\) 个整数,第 \(i\) 个整数表示第 \(i\) 个布丁的颜色 \(a_i\)
接下来 \(m\) 行,每行描述一次操作。每行首先有一个整数 \(op\) 表示操作类型:

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

输出格式

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

样例 #1

样例输入 #1

4 3
1 2 2 1
2
1 2 1
2

样例输出 #1

3
1

提示

样例 1 解释

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

数据规模与约定

对于全部的测试点,保证 \(1 \leq n, m \leq 10^5\)\(1 \leq a_i ,x, y \leq 10^6\)

提示

请注意,不保证颜色的编号不大于 \(n\),也不保证 \(x \neq y\)\(m\) 不是颜色的编号上限。

分析

线段树合并模板,注意不要自己合并自己。

又:链表启发式合并

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100,M=1e6+100;

struct segtree
{
    int lc,rc,val;
    int lef,rig;
}s[N<<6];
struct col
{
    int x,y,op;
}q[N];
int n,m,root[M],tot;
int h[M],ans;

void pushup(int i)
{
    s[i].lef=(s[i].lc>0)?(s[s[i].lc].lef):(s[s[i].rc].lef);
    s[i].rig=(s[i].rc>0)?(s[s[i].rc].rig):(s[s[i].lc].rig);
    s[i].val=s[s[i].lc].val+s[s[i].rc].val-(s[s[i].rc].lef==s[s[i].lc].rig+1);
}
void upd(int &i,int l,int r,int x)
{
    if(!i)i=++tot;
    if(l==r)
    {
        s[i].lef=s[i].rig=l;
        s[i].val=1;
        return ;
    }
    int mid=(l+r)>>1;
    if(x<=mid)
        upd(s[i].lc,l,mid,x);
    else
        upd(s[i].rc,mid+1,r,x);
    pushup(i);
}
void merg(int &i,int &j,int l,int r)
{
    if(i==0 || j==0)
    {
        i+=j;
        return ;
    }
    if(l==r)
    {
        s[i].val=1;
        s[i].lef=s[i].rig=l;
        return ;
    }
    int mid=(l+r)>>1;
    merg(s[i].lc,s[j].lc,l,mid);
    merg(s[i].rc,s[j].rc,mid+1,r);
    pushup(i);
}
void init()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        scanf("%d",h+i);
    for(int i=1;i<=n;++i)
    {
        ans=ans-s[root[h[i]]].val;
        upd(root[h[i]],1,n,i);
        ans=ans+s[root[h[i]]].val;
    }
}

void work()
{
    while(m--)
    {
        int opt,x,y;
        scanf("%d",&opt);
        if(opt==1)
        {
            scanf("%d%d",&x,&y);
            if(x==y)continue;
            ans=ans-s[root[x]].val-s[root[y]].val;
            merg(root[y],root[x],1,n);
            root[x]=0;
            ans+=s[root[y]].val;
        }
        else
            printf("%d\n",ans);
    }
}

int main()
{
    init();
    work();
    return 0;
}









posted @ 2024-11-29 11:14  Glowingfire  阅读(1)  评论(0编辑  收藏  举报