1. 程式人生 > >bzoj 1098 [POI2007]辦公樓biu——連結串列

bzoj 1098 [POI2007]辦公樓biu——連結串列

題目:https://www.lydsy.com/JudgeOnline/problem.php?id=1098

求補圖的連通塊大小。與自己沒有邊的和自己在一個連通塊裡。

用連結串列把所有點串起來。先給自己有邊的打上標記,刪掉自己,然後訪問連結串列裡的元素;沒有標記的就從連結串列裡刪掉並加入棧,對每個棧裡的元素重複這個操作直到棧空。一次弄出一個連通塊。

這樣連結串列裡的元素越刪越少,時間複雜度分析一下的話,每個點被刪掉一次,其餘的訪問是因為有邊,所以總複雜度O(n+m)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include
<algorithm> using namespace std; const int N=1e5+5,M=2e6+5; int n,m,hd[N],xnt,to[M<<1],nxt[M<<1]; int pr[N],nt[N],sta[N],top,vis[N],siz[N],cnt; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='
9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar(); return fx?ret:-ret; } void add(int x,int y) { to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt; to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt; } void del(int cr) { nt[pr[cr]]=nt[cr];pr[nt[cr]]=pr[cr]; } int main() { n=rdn();m=rdn(); for(int i=1,u,v;i<=m;i++) { u
=rdn(); v=rdn(); add(u,v); } for(int i=1;i<=n;i++) pr[i]=i-1,nt[i]=i+1; int cr=1; while(cr<n+1) { siz[++cnt]=1; for(int i=hd[cr];i;i=nxt[i])vis[to[i]]=cr; del(cr); int nw=nt[0]; while(nw<n+1) { if(vis[nw]!=cr)sta[++top]=nw,del(nw),siz[cnt]++; nw=nt[nw]; } while(top) { int k=sta[top--]; for(int i=hd[k];i;i=nxt[i])vis[to[i]]=k; int tw=nt[0]; while(tw<n+1) { if(vis[tw]!=k)sta[++top]=tw,del(tw),siz[cnt]++; tw=nt[tw]; } } cr=nt[0]; } printf("%d\n",cnt); sort(siz+1,siz+cnt+1); for(int i=1;i<=cnt;i++)printf("%d ",siz[i]); printf("\n"); return 0; }