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);//边界设为-1for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=0;//最开始为被询问的编号都默认为0 父亲也是0for(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;//最开始的图连通块个数为1for(int i=1;i<=k;i++) ans[i]+=ans[i-1];for(int i=1;i<=k;i++) cout<<ans[i]<<'\n';return 0;
}