[poi2007] biu

题意:给定一个图,点n<=105,边m<=106,现在求它的补图有多少个联通分量。。

思路:很容易想到并查集,但是补图边太多了。。

        于是只能优化掉一些多余的边。。

       具体做法是用队列优化。。

      刚开始把所有没被连接的点用一个链表连接起来,那么选取一个未扩展得点u,就可以吧链表分成两个,一个就是跟他补图有边的,一个是没边的。。

     并把有边的跟他连接起来。。删除链表。。并放入队列。。

     这样每个点进入队列一次,每条边被访问2次。。时间复杂度为O(n+m)

code:

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<cmath>
  6 #include<algorithm>
  7 #include<string>
  8 #include<map>
  9 #include<set>
 10 #include<vector>
 11 #include<queue>
 12 #include<stack>
 13 #include<ctime>
 14 #define repf(i, a, b) for (int i = (a); i <= (b); ++i)
 15 #define repd(i, a, b) for (int i = (a); i >= (b); --i)
 16 #define M0(x)  memset(x, 0, sizeof(x))
 17 #define Inf  0x7fffffff
 18 #define MP make_pair
 19 #define PB push_back
 20 #define eps 1e-8
 21 #define pi acos(-1.0)
 22 using namespace std;
 23 const int maxn = 120000;
 24 const int maxm = 4200000;
 25 struct edge{
 26       int v, next;
 27 } e[maxm];
 28 struct node{
 29       node *x, *y;
 30 } n1[maxn], n2[maxn], *list[2], *d[2], tr, *null = &tr;
 31 int fa[maxn], last[maxn], tot, n, m;
 32 
 33 void add(const int& u, const int& v){
 34      e[tot] = (edge){v, last[u]}, last[u] = tot++;
 35 }
 36 
 37 void init(){
 38      memset(last, -1, sizeof(last));
 39      int u, v;
 40      for (int i = 0; i < m; ++i){
 41            scanf("%d%d", &u, &v);   
 42            add(u, v), add(v, u);
 43      }     
 44 }
 45 
 46 int sz[maxn], inlist[maxn];
 47 int find(int k){
 48     return fa[k] == k ? k : fa[k] = find(fa[k]); 
 49 }
 50 
 51 void delnode(node *it){
 52      it->x->y = it->y, it->y->x = it->x;
 53      it->x = it->y = null;
 54 }
 55 
 56 void addnode(node *list, node *it){
 57      it->x = list, it->y = list->y;
 58      list->y->x = it, list->y = it;
 59 }
 60 
 61 void clear(int c){
 62      node *uu;
 63      for (node *it = list[c]; it != null;){
 64             uu = it->y;
 65             it->x = it->y = null;
 66             it = uu;
 67      }
 68 }
 69 
 70 void solve(){
 71      M0(n1), M0(n2), M0(inlist);
 72      list[0] = n1 + n + 1, list[1] = n2 + n + 1;
 73      node *it, *uu;
 74      list[0]->x = list[0]->y = null, list[1]->x = list[1]->y = null;
 75      d[0] = n1, d[1] = n2;
 76      int c = 0, u, v;
 77      for (int i = 1; i <= n; ++i) addnode(list[c], d[c] + i), inlist[i] = 1;
 78      for (int i = 1; i <= n; ++i) fa[i] = i;
 79      queue<int> q;
 80      while (list[c]->y != null){
 81          u = list[c]->y - d[c];
 82          q.push(u);
 83          while (!q.empty()){
 84              int s = c ^ 1;
 85              u = q.front(), q.pop();
 86              clear(s);
 87              for (int p = last[u]; ~p; p = e[p].next){
 88                   v = e[p].v;
 89                   if (!inlist[v]) continue;
 90                   delnode(d[c] + v);
 91                   addnode(list[s], d[s] + v);
 92              }
 93              int fu = find(u), fv;
 94              for (it = list[c]->y; it != null; it = it->y){
 95                   v = it - d[c];
 96                   if (!inlist[v]) continue;
 97                   fv = find(v);
 98                   if (fu != fv) fa[fv] = fu;    
 99                   inlist[v] = 0, q.push(v);
100              }
101              c = s;
102          }
103      }
104      M0(sz);
105      for (int i = 1; i <= n; ++i)
106          ++sz[find(i)];
107      vector<int> ans;
108      for (int i = 1; i <= n; ++i) if (sz[i])
109           ans.push_back(sz[i]);
110      sort(ans.begin(), ans.end());
111      printf("%d\n", (int)ans.size());
112      for (int i = 0; i < (int)ans.size(); ++i)
113           i == ans.size()-1 ? printf("%d\n", ans[i]) : printf("%d ", ans[i]);
114      
115 }
116 
117 int main(){
118 //    freopen("a.in", "r", stdin);
119 //    freopen("a.out", "w", stdout);
120     while (scanf("%d%d", &n, &m) != EOF){
121            init();
122            solve();
123     }
124     return 0;
125 }
View Code

 

posted on 2014-11-11 15:10  yzcstc  阅读(172)  评论(0编辑  收藏  举报