[链表] BZOJ1098: [POI2007]办公楼biu

来源:互联网 发布:linux下telnet安装包 编辑:程序博客网 时间:2024/04/28 16:35

太菜了…这都不会…
就是求反图的联通块。就是每次挑一个边在反图bfs出一个块。
我们每次找反图边扩展,是找原图中和当前节点不直接相连的其他点。直接暴力会变成 n2
所以需要把访问过的点马上删去。可以用链表。
这样每个点就只访问一次了。复杂度 O(n+m)

#include<cstdio>#include<queue>#include<cstring>#include<algorithm>using namespace std;const int maxn=100005,maxe=4000005;int n,m,ans[maxn];int fir[maxn],nxt[maxe],son[maxe],tot;void add(int x,int y){    son[++tot]=y; nxt[tot]=fir[x]; fir[x]=tot;}int vis[maxn],clk;int L[maxn],R[maxn],tem[maxn];void Del(int x){ R[L[x]]=R[x]; L[R[x]]=L[x]; }queue<int> que;void bfs(){    int x=R[0]; Del(x); que.push(x); ans[++ans[0]]=1;    while(!que.empty()){        int x=que.front(); que.pop(); clk++;        for(int j=fir[x];j;j=nxt[j]) vis[son[j]]=clk;        tem[0]=0;        for(int i=R[0];i!=n+1;i=R[i]) if(vis[i]!=clk){            que.push(i); tem[++tem[0]]=i; ans[ans[0]]++;        }        for(int i=1;i<=tem[0];i++) Del(tem[i]);    }}int main(){    freopen("bzoj1098.in","r",stdin);    freopen("bzoj1098.out","w",stdout);    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++){        int x,y; scanf("%d%d",&x,&y);        add(x,y); add(y,x);    }    R[0]=1; for(int i=1;i<=n;i++) L[i]=i-1, R[i]=i+1; L[n+1]=n;    while(R[0]!=n+1) bfs();    sort(ans+1,ans+1+ans[0]);     printf("%d\n",ans[0]);    for(int i=1;i<=ans[0];i++) printf("%d ",ans[i]);    return 0;}