bzoj 1098 办公楼biu —— 链表+栈

题目: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\n",cnt);
  sort(siz+1,siz+cnt+1);
  for(int i=1;i<=cnt;i++)printf("%d ",siz[i]);
  return 0;
}

 

posted @ 2018-10-18 15:12  Zinn  阅读(188)  评论(0编辑  收藏  举报