Evanyou Blog 彩带

P3201 [HNOI2009]梦幻布丁

题目描述

N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.

输入输出格式

输入格式:

第一行给出N,M表示布丁的个数和好友的操作次数. 第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,对于每个操作,若第一个数字是1表示要对颜色进行改变,其后的两个整数X,Y表示将所有颜色为X的变为Y,X可能等于Y. 若第一个数字为2表示要进行询问当前有多少段颜色,这时你应该输出一个整数. 0

输出格式:

针对第二类操作即询问,依次输出当前有多少段颜色.

输入输出样例

输入样例#1: 
4 3
1 2 2 1
2
1 2 1
2
输出样例#1: 
3
1

说明

1<=n,m<=100,000; 0<Ai,x,y<1,000,000

 

Solution:

  本题平衡树+启发式合并。

  考试的时候没有注意$x==y$的情况,结果只对了8个点,GG。

  思路比较简单,我们用多棵平衡树维护每种颜色的下标,在对一种颜色的平衡树进行操作的同时可以处理出这个颜色的连续段数,具体来说,对于一种颜色,若新加入一个节点,就判断该节点能否与其前趋或者后继相接,讨论一下各种情况就好了。

  在合并的时候用启发式的思想把节点少的树的每个节点暴力加入节点多的树中,若$size$少的树是变换后的颜色所在树,直接交换两种颜色的树根就可以了。

  查询就直接输出全局的答案。

  时间复杂度:$O(n\log ^2 n)$

  (坑点:$x==y$时不需要操作)

代码:

/*Code by 520 -- 10.25*/
#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define il inline
#define ll long long
#define RE register
#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
#define debug printf("%s %d\n",__FUNCTION__,__LINE__)
using namespace std;
const int N=1e6+5;
int n,m,root[N],ans,sum[N];
int col[N],ch[N][2],cnt,fa[N],rnd[N],siz[N],date[N];

int gi(){
    int a=0;char x=getchar();
    while(x<'0'||x>'9') x=getchar();
    while(x>='0'&&x<='9') a=(a<<3)+(a<<1)+(x^48),x=getchar();
    return a;
}

il int newnode(int v){
    ++cnt;
    date[cnt]=v,rnd[cnt]=rand(),siz[cnt]=1,fa[cnt]=0;
    return cnt;
}

il void up(int rt){
    if(ch[rt][0]) fa[ch[rt][0]]=rt;
    if(ch[rt][1]) fa[ch[rt][1]]=rt; 
    siz[rt]=siz[ch[rt][0]]+siz[ch[rt][1]]+1;
}

int merge(int x,int y){
    if(!x||!y) return x+y;
    if(rnd[x]<rnd[y]) {ch[x][1]=merge(ch[x][1],y),up(x);return x;}
    else {ch[y][0]=merge(x,ch[y][0]),up(y);return y;}
}

void split(int rt,int v,int &x,int &y){
    if(!rt) {x=y=0;return ;}
    if(date[rt]>v) y=rt,split(ch[rt][0],v,x,ch[y][0]),up(y);
    else x=rt,split(ch[rt][1],v,ch[x][1],y),up(x);
}

il int kth(int rt,int v){
    while(1){
        if(siz[ch[rt][0]]>=v) rt=ch[rt][0];
        else if(siz[ch[rt][0]]+1<v) v-=siz[ch[rt][0]]+1,rt=ch[rt][1];
        else return rt;
    }
}

il int pre(int id,int v){
    int x,y,ans; split(root[id],date[v]-1,x,y);
    ans=(!siz[x])?-1:(kth(x,siz[x]));
    root[id]=merge(x,y),fa[root[id]]=0;
    return ans;
}

il int suc(int id,int v){
    int x,y,ans; split(root[id],date[v],x,y);
    ans=(!siz[y])?-1:(kth(y,1));
    root[id]=merge(x,y),fa[root[id]]=0;
    return ans;
}

il void ins(int id,int v){
    int x=pre(id,v),y=suc(id,v);
    if(x>0&&y>0&&date[x]==date[v]-1&&date[y]==date[v]+1) ans--,sum[id]--;
    else if(x<0&&y<0||x>0&&date[x]!=date[v]-1&&(y<0||date[y]!=date[v]+1)||y>0&&date[y]!=date[v]+1&&(x<0||date[x]!=date[v]-1)) ans++,sum[id]++;
    x=y=0;
    split(root[id],date[v]-1,x,y),root[id]=merge(merge(x,v),y),fa[root[id]]=0;
}

void dfs(int id,int rt){
    if(!rt) return;
    dfs(id,ch[rt][0]),dfs(id,ch[rt][1]);
    ch[rt][0]=ch[rt][1]=0;
    ins(id,rt);
}

int main(){
    n=gi(),m=gi(); int opt,x,y;
    For(i,1,n) {
        col[i]=gi();
        ins(col[i],newnode(i));
    }
    while(m--){
        opt=gi();
        if(opt==1) {
            x=gi(),y=gi();
            if(x==y) continue;
            else if(siz[root[x]]<=siz[root[y]]){
                dfs(y,root[x]),root[x]=0;ans-=sum[x],sum[x]=0;
            }
            else {
                swap(root[x],root[y]),swap(sum[x],sum[y]);
                dfs(y,root[x]),root[x]=0;ans-=sum[x],sum[x]=0;
            }
        }
        else printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2018-10-31 22:59  five20  阅读(220)  评论(0编辑  收藏  举报
Live2D