liluhexuan
2024-11-18 12:31:43
我不会告诉你我在结束后 8 分钟做出来了
这个题可以将一些颜色相同的相邻单元格抽象成一个集合(也就是后面的一个块),而改变颜色就是改变这个块的颜色标签。于是想到并查集,我们来一步一步将问题逐个击破。
这里规定一下,一个块的老大指的是并查集的每一个集合中
在引入一个数组
考虑并查集的合并。如果改变颜色后与相邻的块颜色相同,那就将这两个块合并为一个新的块。于是写出这样的代码:
if(x>1&&col[find(x-1)]==c) join(x-1,x);
if(x<n&&col[find(x+1)]==c) join(x,x+1);
但如果这么写是会出问题的(我就被这里卡了)。如果我们修改一个长度为三的块的中间为相邻块的颜色,那理论上这两个块应该合并。但由于
为了防止出现这个错误,我们可以引入一个
我们需要引入两个数组:
我们考虑当一个块变色时,这个块原来的颜色应该会减少这个块的点的数量,而新的颜色应该会增加这个块的节点数量。也就是代码中的
s[col[find(x)]]-=sum[find(x)];
s[c]+=sum[find(x)];
好了,终于可以放代码了:
#include<iostream>
using namespace std;
#define int long long //可有可无
const int N=5e5+10;
int p[N],col[N],sum[N],s[N],mn[N],mx[N];
int find(int x){ //非常标准的并查集
if(x==p[x]) return x;
return p[x]=find(p[x]);
}
void join(int x,int y){
x=find(x),y=find(y);
if(x!=y) sum[x]+=sum[y],p[y]=x; //一定要判定 x!=y
mx[x]=max(mx[x],mx[y]); //更新最小节点编号
mn[x]=min(mn[x],mn[y]); //更新最大节点编号
}
bool query(int x,int y){
return find(x)==find(y);
}
signed main(){
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++) //像屎的初始化
p[i]=i,col[i]=i,sum[i]=1,s[i]=1,mn[i]=i,mx[i]=i;
while(q--){
int op;
cin>>op;
if(op==1){
int x,c;
cin>>x>>c;
s[col[find(x)]]-=sum[find(x)]; //统计答案
s[c]+=sum[find(x)];
col[find(x)]=c; //变色
//合并
if(x>1&&col[find(mn[find(x)]-1)]==c) join(mn[find(x)]-1,x);
if(x<n&&col[find(mx[find(x)]+1)]==c) join(x,mx[find(x)]+1);
}
else{
int c; //这要还看不懂就可以退役了
cin>>c;
cout<<s[c]<<endl;
}
}
return 0; //拜拜程序
}
最后说个注意事项: