P3201 [HNOI2009] 梦幻布丁

P3201 [HNOI2009] 梦幻布丁

题目描述

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

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

数据规模与约定

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

提示

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

Solution:

感觉是好早之前做过的题了,幸好当年在代码里面加了不少注释,极大的减轻了我复健时写解题报告的压力。(*≧ω≦)

首先我们观察题目不难发现这个染色操作有些许诡异,他是对于一种颜色的所有节点进行一个换色的操作,然后我们需要统计的东西是当前全局上的颜色段数。

我们发现,维护同颜色段这个事情是要在一个连续的区间上进行的,只有两个相邻的不同颜色格子变为同色时答案才会改变,所以我们只关心每次操作过后被合并的两种颜色中有那些地方是相邻的。这启示我们使用线段树合并解决此题。

首先我们对每种颜色开一颗线段树,线段树的区间 \([l,r]\) 代表着编号在 \([l,r]\) 内的所有点都是同一个颜色 \(Col\) 。那么变色这个操作实际上就是将原色 \(x\) 所代表的线段树与新色 \(y\) 合并。然后 \(pushup\) 维护下那些相邻的节点融合成了一个连段段。

然后这题就做完了
(*^3^)

Code:

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int be[N<<5],ed[N<<5],ls[N<<5],rs[N<<5],rt[N<<5],sum[N<<5];
//be[x]:在x号区间内,最左一个被涂上相应颜色的格子的坐标 ed:最右一个
//ls[x],rs[x]:x节点的左,右子树的根的编号
//sum[x]:x节点内相应的颜色被分成了几段
int n,m,cnt;
void up(int x)
{
    //统一颜色的相邻两个区间尝试合并
    //相邻是指其在链表或者线段树上的关系,实际坐标中不一定相邻
    if(!be[ls[x]])be[x]=be[rs[x]];
    else be[x]=be[ls[x]];
    if(!ed[rs[x]])ed[x]=ed[ls[x]];
    else ed[x]=ed[rs[x]];
    sum[x]=sum[ls[x]]+sum[rs[x]];
    if(ed[ls[x]]+1==be[rs[x]])sum[x]--;
}
void upd(int &x,int l,int r,int pos)
{
    //在rt[x]这个根上新建一个[wz,wz]的叶子节点
    //对于每个线段树的节点,他储存着一段区间[l,r]上那些区间内被涂上了x颜色
    x= (x ? x : ++cnt);int mid=l+r>>1;
    if(l==r) { be[x]=ed[x]=pos;sum[x]=1; return;}
    if(pos<=mid)upd(ls[x],l,mid,pos);
    else upd(rs[x],mid+1,r,pos); up(x);
}
int merge(int x,int y,int l,int r)
{
    //x:原x树 T:合并后的树(即新x) y:被合并到T上的树
    //T只是为了方便理解yy出来的一个东西,实际操作中直接在x上合并y即可得到T 
    //如果x没有这部分[l,r]的子树,则直接把y在[l,r]上的这部分直接抢过来作为T在[l,r]上的部分子树
    //若y无此部分则T在在此部分[l,r]的子树为x在此部分的子树
    if(!x||!y)return x|y;
    if(l==r)//类似于重新建立此叶子节点然后再跑合并
    {
        be[x]=ed[x]=l;sum[x]=1;return x;
    }
    int mid=l+r>>1;
    ls[x]=merge(ls[x],ls[y],l,mid);
    rs[x]=merge(rs[x],rs[y],mid+1,r);
    up(x);return x;//合并
}
long long ans;
int main()
{
    //freopen("pudding.in","r",stdin);
    //freopen("pudding.out","w",stdout);
    cin>>n>>m;
    for(int i=1,type;i<=n;i++)
    {
        scanf("%d",&type);
        ans-=sum[rt[type]];
        upd(rt[type],1,n,i);
        ans+=sum[rt[type]];
    }
    for(int i=1,type,x,y;i<=m;i++)
    {
        scanf("%d",&type);
        if(type==1)
        {
            scanf("%d%d",&x,&y);
            if(x==y)continue;
            ans-=sum[rt[x]];
            ans-=sum[rt[y]];
            rt[y]=merge(rt[y],rt[x],1,n);
            rt[x]=0;
            ans+=sum[rt[y]];
        }
        else
        {
            printf("%lld\n",ans);
        }
    }
    return 0;
}
posted @ 2025-04-10 13:08  liuboom  阅读(15)  评论(0)    收藏  举报