F. Number of Components
并查集,每次修改考虑的是这个修改带来的贡献,就是和相邻颜色的对比,如果不考虑先不考虑颜色覆盖,那么添加颜色首先会产生一个新的连通块,然后考虑合并,每合并一次就会减少一个连通块。
题目转化成了对每一种颜色考虑,然后看这个颜色的改变。
对于每种颜色,会有两种:添加一个颜色(正序处理),颜色被覆盖(逆序处理)
而对于颜色被覆盖,只需要逆序考虑即可。每次只考虑贡献,因此最终要求个前缀和。
#include<bits/stdc++.h>
using namespace std;
constexpr int N=310,M=2000010;
int fa[2*M];
int n,m,k;
int id[N][N],idx;
int a[N][N],cur;
int ans[M];
struct node
{
int x,y;
int fr,to;
}q[M];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void merge(int x,int y)
{
x=find(x),y=find(y);
if(x==y) return;
fa[x]=y; cur--;
}
void solve(int x,int y)// 由于边界都设为-1 不用考虑出界问题
{
if(a[x][y]==a[x-1][y]) merge(id[x][y],id[x-1][y]);
if(a[x][y]==a[x+1][y]) merge(id[x][y],id[x+1][y]);
if(a[x][y]==a[x][y-1]) merge(id[x][y],id[x][y-1]);
if(a[x][y]==a[x][y+1]) merge(id[x][y],id[x][y+1]);
}
int main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
cin>>n>>m>>k;
// 询问
for(int i=1;i<=k;i++)
{
int x,y,c;
cin>>x>>y>>c;
q[i]={x,y,a[x][y],c};
a[x][y]=c;
}
memset(a,0xff,sizeof a);//边界设为-1
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=0;
//最开始为被询问的编号都默认为0 父亲也是0
for(int i=1;i<=k;i++)
if(q[i].fr!=q[i].to)// 如果改变颜色
{
cur=1;// 新产生的连通块
a[q[i].x][q[i].y]=q[i].to;
id[q[i].x][q[i].y]=++idx;
fa[idx]=idx;
solve(q[i].x,q[i].y);// 合并一次 cur--
ans[i]+=cur;
}
// 正着做完后a已经是最终的状态
idx=0;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) {id[i][j]=++idx;fa[idx]=idx;}
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) solve(i,j);
// 逆着在做一次
for(int i=k;i>=1;i--)
if(q[i].fr!=q[i].to)
{
cur=1;
a[q[i].x][q[i].y]=q[i].fr;
id[q[i].x][q[i].y]=++idx;
fa[idx]=idx;
solve(q[i].x,q[i].y);
ans[i]-=cur;
}
ans[0]=1;//最开始的图连通块个数为1
for(int i=1;i<=k;i++) ans[i]+=ans[i-1];
for(int i=1;i<=k;i++) cout<<ans[i]<<'\n';
return 0;
}