ABC229E Graph Destruction
一道比较套路的题,没有太多的思维量,也没有考察很难的算法。
解法
看到题中有求连通块,我们就可以用并查集来维护连通块。又因为普通并查集只支持添加操作,不支持撤销操作,所以我们思考一个方向的做法。题中让我们正着删点,我们就倒着加点就可以了。现在条件就被我们转化为了如下 3 3 3 条:
- 给定 n n n 个点, m m m 条边等待被添加,现在拥有一个空图。
- 按照 n , n − 1 , … , 1 n,n-1,\dots,1 n,n−1,…,1 的顺序添加点及与其相连且另一个点的编号大于该点编号的边。
- 对于每次加点操作,求出现在图中连通块的数量并记录,因为我们最后要正着输出。
具体执行
按照如上 3 3 3 条,我们可以按照如下思路统计连通块个数:
- 如果在图外加了一个点,则连通块数量加 1 1 1。
- 加点后我们再加边,如果加完边沟通了两个不同的连通块,则连通块数量减 1 1 1。
最后代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Getchar() p1==p2 and (p2=(p1=Inf)+fread(Inf,1,1<<21,stdin),p1==p2)?EOF:*p1++
#define Putchar(c) p3==p4 and (fwrite(Ouf,1,1<<21,stdout),p3=Ouf),*p3++=c
char Inf[1<<21],Ouf[1<<21],*p1,*p2,*p3=Ouf,*p4=Ouf+(1<<21);
inline void read(int &x,char c=Getchar())
{
bool f=c!='-';
x=0;
while(c<48 or c>57) c=Getchar(),f&=c!='-';
while(c>=48 and c<=57) x=(x<<3)+(x<<1)+(c^48),c=Getchar();
x=f?x:-x;
}
inline void write(int x)
{
if(x<0) Putchar('-'),x=-x;
if(x>=10) write(x/10),x%=10;
Putchar(x^48);
}
int n,m,head[200010],cnt,fa[200010],ans[200010],tot;
struct edge
{
int to,next;
};
edge e[400010];
inline void add(const int &x,const int &y)
{
e[++cnt].to=y,e[cnt].next=head[x],head[x]=cnt;
}
inline int find(int x)
{
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
int main()
{
read(n),read(m);
for(int i=1,x,y;i<=m;i++) read(x),read(y),add(x,y),add(y,x);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=n;i;i--)
{
ans[i]=tot,tot++;
for(int j=head[i];j;j=e[j].next)
if(e[j].to>i && find(e[j].to)!=find(i)) fa[fa[e[j].to]]=fa[i],tot--;
}
for(int i=1;i<=n;i++) write(ans[i]),Putchar('\n');
fwrite(Ouf,1,p3-Ouf,stdout),fflush(stdout);
return 0;
}