[CF1303F] Number of Components
\(\text{Problem}:\)题目链接
\(\text{Solution}:\)
观察 \(c_{i}\leq c_{i+1}\) 的性质。这说明,对于一个颜色 \(C\),所有 \(a_{i,j}=C\) 的位置一定是先全部覆盖,然后再逐个删除。
对于一次操作,记颜色 \(C_{2}\) 覆盖掉了 \(C_{1}\),原图为 \(A\),新图为 \(B\)。从 \(A\rightarrow B\),相当于颜色 \(C_{2}\) 的合并;从 \(A\rightarrow B\),相当于颜色 \(C_{1}\) 的分裂。逆向考虑,发现从 \(B\rightarrow A\),相当于颜色 \(C_{1}\) 的合并。我们发现,把一个操作分为两个操作后,由于满足第一段中 \(c_{i}\leq c_{i+1}\) 得到的性质,一种颜色 \(C\) 一定是合并结束后再分裂。那么答案对于一种颜色是独立的!所以,我们可以对于每种颜色单独考虑贡献,把分裂看作反向合并,对答案贡献也取反即可。
此题由于空间限制较紧,需要较好的实现方式。具体的,只需要开 \(O(n^2)\) 个并查集。对于每种颜色算完之后,把与这种颜色有关的位置上的 \(f_{i,j}\) 清空即可。
\(\text{Code}:\)
#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std; const int N=310, M=2000010;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,m,Q,Ans[M],a[N][N],book[N*N];
int f[N*N];
inline int Find(int x) { if(!x) return 0; return f[x]^x?f[x]=Find(f[x]):x; }
inline int Merge(int x,int y)
{
int fx=Find(x), fy=Find(y);
if(fx==fy) return 0;
f[fx]=fy;
return 1;
}
inline int ID(int x,int y) { return (x-1)*m+y; }
struct Node { int x,y,id; };
vector<Node> g1[M],g2[M];
const int step[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
signed main()
{
n=read(), m=read(), Q=read();
int Mx=0;
for(ri int i=1;i<=Q;i++)
{
int x,y,c;
x=read(), y=read(), c=read();
Mx=max(Mx,c);
if(a[x][y]^c)
{
g1[a[x][y]].eb((Node){x,y,i});
a[x][y]=c;
g2[a[x][y]].eb((Node){x,y,i});
}
}
for(ri int i=1;i<=n;i++)
for(ri int j=1;j<=m;j++)
g1[a[i][j]].eb((Node){i,j,Q+1});
for(ri int i=0;i<=Mx;i++) reverse(g1[i].begin(),g1[i].end());
for(ri int i=0;i<=Mx;i++)
{
for(auto j:g1[i])
{
book[ID(j.x,j.y)]=1;
if(!f[ID(j.x,j.y)]) f[ID(j.x,j.y)]=ID(j.x,j.y);
Ans[j.id]--;
for(ri int k=0;k<4;k++)
{
int u=j.x+step[k][0];
int v=j.y+step[k][1];
if(u<1||u>n||v<1||v>m||!book[ID(u,v)]) continue;
Ans[j.id]+=Merge(ID(u,v),ID(j.x,j.y));
}
}
for(auto j:g1[i]) book[ID(j.x,j.y)]=f[ID(j.x,j.y)]=0;
}
for(ri int i=0;i<=Mx;i++)
{
for(auto j:g2[i])
{
book[ID(j.x,j.y)]=1;
if(!f[ID(j.x,j.y)]) f[ID(j.x,j.y)]=ID(j.x,j.y);
Ans[j.id]++;
for(ri int k=0;k<4;k++)
{
int u=j.x+step[k][0];
int v=j.y+step[k][1];
if(u<1||u>n||v<1||v>m||!book[ID(u,v)]) continue;
Ans[j.id]-=Merge(ID(u,v),ID(j.x,j.y));
}
}
for(auto j:g2[i]) book[ID(j.x,j.y)]=f[ID(j.x,j.y)]=0;
}
Ans[0]=1;
for(ri int i=1;i<=Q;i++)
{
Ans[i]+=Ans[i-1];
printf("%d\n",Ans[i]);
}
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。