标签:导致 办公楼 表示 main 证明 联系 连通 扩展 swap
[BZOJ1098][POI2007]办公楼biu
试题描述
输入
输出
输入示例
7 16 1 3 1 4 1 5 2 3 3 4 4 5 4 7 4 6 5 6 6 7 2 4 2 7 2 5 3 5 3 7 1 7
输出示例
3 1 2 4
数据规模及约定
见“输入”
题解
显然如果能够建立原图的反图,那么一个连通块的所有点放在同一个办公楼就好了。但是点数太多,导致反图的边数巨大而无法存储。
于是可以使用 BFS + 链表优化,就是你每次扩展的时候把原图中连出的点打上标记,然后暴力看一下链表中还剩哪些点,然后往那些点转移就好了,转移到的点直接从链表删除,因为我们无须重复考虑同一个点。
看上去这个应该很快,然而我并不会严格证明复杂度。。。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); } return x * f; } #define maxn 100010 #define maxm 4000010 int n, m, head[maxn], nxt[maxm], to[maxm], Pre[maxn], Nxt[maxn]; void AddEdge(int a, int b) { to[++m] = b; nxt[m] = head[a]; head[a] = m; swap(a, b); to[++m] = b; nxt[m] = head[a]; head[a] = m; return ; } bool vis[maxn], tag[maxn]; int Q[maxn], hd, tl, ans, tot[maxn]; void del(int u) { int lp = Pre[u], ln = Nxt[u]; Nxt[lp] = ln; Pre[ln] = lp; vis[u] = 1; return ; } void bfs(int s) { hd = tl = 0; Q[++tl] = s; del(s); tot[ans] = 1; while(hd < tl) { int u = Q[++hd]; for(int e = head[u]; e; e = nxt[e]) tag[to[e]] = 1; for(int i = Nxt[0]; i; i = Nxt[i]) if(!tag[i]) del(i), tot[ans]++, Q[++tl] = i; for(int e = head[u]; e; e = nxt[e]) tag[to[e]] = 0; } return ; } int main() { n = read(); int m = read(); for(int i = 1; i <= m; i++) { int a = read(), b = read(); AddEdge(a, b); } for(int i = 0; i < n; i++) Nxt[i] = i + 1, Pre[i+1] = i; for(int i = 1; i <= n; i++) if(!vis[i]) ans++, bfs(i); sort(tot + 1, tot + ans + 1); printf("%d\n", ans); for(int i = 1; i <= ans; i++) printf("%d ", tot[i]); return 0; }
标签:导致 办公楼 表示 main 证明 联系 连通 扩展 swap
原文地址:http://www.cnblogs.com/xiao-ju-ruo-xjr/p/6654386.html