bzoj1098

来源:互联网 发布:金字塔软件下载 编辑:程序博客网 时间:2024/06/06 04:07

这道是同学出给初一孩子的题, 我看了看然后果断不会。。。

求连通块应该是用bfs或并查集的, 并查集N^2的边貌似做不了, 然后优化的方式我也不知道了。

BFS的话应该是每加入到当前连通块中一个点就要访问其余所有点看看能不能被到达如果不能的话就加入到这个连通块中。

这样做是N^2的! 所以更好的方法?

优化的第一点是这道题的M是比较小的, 所以与其枚举不能访问的点不如枚举可以访问的点, 第二是在点集中一个点即使被访问过我们还是要再访问一次然后再发现访问过, 返回, 如果建一个链表就可以把这些访问过的点跳过啦! 

/*

那就是我们先把所有的节点挂链,然后再把链表上的一个节点入队,遍历其在原图上相邻的点并做上标记,那么这时没有打上标记的点在补图上和当前节点一定有边相连因而一定在同一个联通块中,所以再把这些没有打上标记的点入队,并且在链表中除去,继续这个过程,直到队列为空时这个联通块就找出来了,再取链表上还存在的点入队寻找一个新的联通块,直到删掉所有点为止,复杂度降为了O(n + m)。

*/

#include <iostream>#include <cstdio>#include <algorithm>#include <queue>#define MAXN 100005#define MAXM 2000005using namespace std;int n, m, e, head[MAXN], z[MAXN], zz;struct Edge{    int to, next, pre;    }edge[MAXM * 2], l[MAXN];void addedge(int s, int t){edge[++ e].to = t; edge[e].next = head[s]; head[s] = e;    }void del(int x){    l[l[x].pre].next = l[x].next;    l[l[x].next].pre = l[x].pre;    }bool b[MAXN], qb[MAXN];queue<int>q;void bfs(){    while(l[0].next){        int now = l[0].next, ans = 1;        del(now);           q.push(now); qb[now] = 1;        while(!q.empty()){            int u = q.front(); q.pop();            for(int i = head[u]; i != -1; i = edge[i].next)b[edge[i].to] = 1;                for(int i = l[0].next; i; i = l[i].next)                if(!b[i] && !qb[i]){                    qb[i] = 1;                    q.push(i);                    ans ++;                    del(i);                    }for(int i = head[u]; i != -1; i = edge[i].next)b[edge[i].to] = 0;        } z[++ zz] = ans;    }    }int main(){    scanf("%d%d", &n, &m);    memset(head, -1, sizeof(head));    for(int i = 1; i <= m; i ++){        int a, b; scanf("%d%d", &a, &b);        addedge(a, b);     addedge(b, a);    }    for(int i = 1; i <= n; i ++){        l[i - 1].next = i; l[i].pre = i - 1;        } l[n].next = 0;    bfs();    sort(z + 1, z + zz + 1);    printf("%d\n", zz);    if(zz){for(int i = 1; i < zz; i ++)printf("%d ", z[i]);printf("%d", z[zz]);    }    return 0;}


0 0
原创粉丝点击