题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1098
首先,没有连边的人一定得在一个连通块里;
先把所有人连成一个链表,然后从第一个人开始,把和它有连边的人都打上标记,没有标记的就加入栈里,并在链表中删除;
只要栈里还有值,就重复这个操作,把必须和栈顶元素在一个连通块的元素也都找出来加入栈,同时 siz++;
因为链表维护,所以遍历人的复杂度总体是 O(n) 的,再加上遍历边的复杂度,算下来是 O(n+m);
关键在于用链表降低遍历人的复杂度!很好的思路。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int const xn=1e5+5,xm=2e6+5; int n,m,hd[xn],ct,to[xm<<1],nxt[xm<<1],vis[xn],pr[xn],nt[xn],siz[xn],cnt; int sta[xn],top; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar(); return f?ret:-ret; } void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;} void del(int x){nt[pr[x]]=nt[x]; pr[nt[x]]=pr[x];} int main() { n=rd(); m=rd(); for(int i=1,x,y;i<=m;i++) { x=rd(); y=rd(); add(x,y); add(y,x); } for(int i=1;i<=n;i++)pr[i]=i-1,nt[i]=i+1; int i=1; nt[0]=1; pr[n+1]=n; while(nt[0]!=n+1) { i=nt[0]; del(i); for(int j=hd[i];j;j=nxt[j])vis[to[j]]=i; sta[++top]=i; siz[++cnt]=1; while(top) { int x=sta[top]; top--; for(int k=hd[x];k;k=nxt[k])vis[to[k]]=x; vis[x]=x; int nww=nt[0]; while(nww!=n+1) { if(vis[nww]!=x)siz[cnt]++,sta[++top]=nww,del(nww); nww=nt[nww]; } } } printf("%d ",cnt); sort(siz+1,siz+cnt+1); for(int i=1;i<=cnt;i++)printf("%d ",siz[i]); return 0; }