Description
N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.
例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.
Input
第一行给出N,M表示布丁的个数和好友的操作次数.
第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,
对于每个操作,
若第一个数字是1表示要对颜色进行改变,其后的两个整数X,Y表示将所有颜色为X的变为Y,X可能等于Y.
若第一个数字为2表示要进行询问当前有多少段颜色,这时你应该输出一个整数. 0。
n,m<=1000000
Output
针对第二类操作即询问,依次输出当前有多少段颜色.
Sample Input
4 3
1 2 2 1
2
1 2 1
2
1 2 2 1
2
1 2 1
2
Sample Output
3
1
1
调了一下午终于A掉了qwq……
学会启发式合并后,其实这个题就是裸题了
splay启发式合并,听起来很智能,只不过就是暴力将小splay上的点一个个insert到大splay上
这个题只需要将需要改的颜色的splay合并到目标颜色splay上就好了
(这个程序并没有从小的合并到大的,不过仍然很快,可能是数据太弱了)
只不过程序中有一些小细节需要注意就是了
细节详见code
学会启发式合并后,其实这个题就是裸题了
splay启发式合并,听起来很智能,只不过就是暴力将小splay上的点一个个insert到大splay上
这个题只需要将需要改的颜色的splay合并到目标颜色splay上就好了
(这个程序并没有从小的合并到大的,不过仍然很快,可能是数据太弱了)
只不过程序中有一些小细节需要注意就是了
细节详见code
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define N (1000000+100) using namespace std; int Size[N],Key[N];//key[i]中存的是编号为i的颜色,用序列的编号大小来建splay,节点里存颜色 int Father[N],Son[N][2]; int n,m,Root[N],ans,a[N]; int flag; int Get(int x){return Son[Father[x]][1]==x;} void Update(int x){Size[x]=Size[Son[x][0]]+Size[Son[x][1]]+1;} void Clear(int x){Key[x]=Father[x]=Son[x][0]=Son[x][1]=Size[x]=0;} void Rotate(int x) { int wh=Get(x); int fa=Father[x],fafa=Father[fa]; Son[fa][wh]=Son[x][wh^1]; Father[fa]=x; if (Son[fa][wh]) Father[Son[fa][wh]]=fa; Son[x][wh^1]=fa; Father[x]=fafa; if (fafa) Son[fafa][Son[fafa][1]==fa]=x; Update(fa); Update(x); } void Splay(int x,int &root) { for (int fa;fa=Father[x];Rotate(x)) if (Father[fa]) Rotate(Get(fa)==Get(x)?fa:x); root=x; } void Insert(int x,int color,int &root) { if (root==0) { Size[x]=1; Key[x]=color; root=x; return; } int now=root,fa=0; while (1) { fa=now;now=Son[now][x>now]; if (now==0) { Size[x]=1; Key[x]=color; Father[x]=fa; Son[fa][x>fa]=x; Update(fa); Splay(x,root); return; } } } void Change(int x,int &root,int color)//唯一需要注意的函数,参数:(被拆的splay的当前节点,合并到的splay的根,需要改成什么颜色)。按中序遍历将要拆的splay一个个拆掉就好 { if (Son[x][0]) Change(Son[x][0],root,color); int rson=Son[x][1];//因为下面要clear x这个节点的信息,所以事先存一下 int pre_color=Key[x];//同上 if (!flag && Key[x]!=Key[x-1]) flag=1+(color==Key[x-1]);//☆如果x是连续序列的开始,就把flag改成1。如果不仅是开始,还能和前一个连起来,flag再加一。(flag在连续序列结束的地方会用到) Clear(x); Insert(x,color,root);//插入到目标splay中 if (Key[x]==Key[x+1]) ans-=1;//如果当前是连续序列的结束且这个点能和后面的颜色接上,ans--。 if (Key[x+1]!=pre_color) ans-=flag-1,flag=0;//如果这个点是结束,ans根据开头是否接上判断减不减。flag归零。 if (rson) Change(rson,root,color); } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;++i) { scanf("%d",&a[i]); if (a[i]!=a[i-1]) ans++; Insert(i,a[i],Root[a[i]]); } int opt,x,y; for (int i=1;i<=m;++i) { scanf("%d",&opt); if (opt==1) { scanf("%d%d",&x,&y); if (Root[x]==0 || x==y) continue;//如果当前颜色没有节点 或者 这次更改前后的颜色是一样的 就不用做了(因为做了会卡) Change(Root[x],Root[y],y); Root[x]=0; } if (opt==2) printf("%d\n",ans); } }