[CF1303F] Number of Components - 并查集,时间倒流

有一个 \(n \times m\) 矩阵,初态下全是 \(0\)。

如果两个相邻元素(四连通)相等,我们就说它们是连通的,且这种关系可以传递。

有 \(q\) 次操作,每次指定一个位置 \((x_i,y_i)\) 把它替换为 \(c_i\)。

每次操作后求这个矩阵有多少个连通块。

\(q \leq 2\times 10^6\), \(n,m \leq 300\)

Solution

带删除的并查集问题可以离线,所以正着倒着各做一次,然后将答案做差就可以了

考虑正着做的过程,刚开始就是一块 \(0\) 的板板

每次我们创建一个新节点,然后试图将它与周围的节点合并,设合并的次数为 \(t\),那么这一次对答案的贡献(即这次操作新增了多少个连通块)就是 \(1-t\)

反向操作时同理,贡献带个负号就可以了

最后输出答案的时候,把贡献数组求个前缀和即可

#include <bits/stdc++.h>
using namespace std;

const int N = 305, M = 2000005;
int a[N][N],n,m,q,ind,num,f[M*2],ans[M],id[N][N];

struct query {
    int x,y,b,c;
} s[M];

int find(int x) {return (x==f[x])?x:f[x]=find(f[x]);}
void merge(int x,int y) {if((x=find(x))-(y=find(y))) f[x]=y,--num;}

void solve(int x,int y) {
    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]);
}

signed main() {
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=q;i++) {
        int x,y,c;
        scanf("%d%d%d",&x,&y,&c);
        s[i]={x,y,a[x][y],c};
        a[x][y]=c;
    }
    memset(a,0xff,sizeof a);
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=0;
    for(int i=1;i<=q;i++) {
        if(s[i].b!=s[i].c) {
            int x=s[i].x,y=s[i].y,b=s[i].b,c=s[i].c;
            num=1;
            a[x][y]=c;
            id[x][y]=++ind;
            f[ind]=ind;
            solve(x,y);
            ans[i]+=num;
        }
    }
    ind=0;
    memset(f,0,sizeof f);
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) {
        id[i][j]=++ind;
        f[ind]=ind;
    }
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) solve(i,j);
    for(int i=q;i>=1;--i) {
        if(s[i].b!=s[i].c) {
            int x=s[i].x,y=s[i].y,b=s[i].c,c=s[i].b;
            num=1;
            a[x][y]=c;
            id[x][y]=++ind;
            f[ind]=ind;
            solve(x,y);
            ans[i]-=num;
        }
    }
    ans[0]=1;
    for(int i=1;i<=q;i++) ans[i]+=ans[i-1];
    for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
}
上一篇:leetcode 删除数组中的重复项


下一篇:2020牛客寒假算法基础集训营4