标签:导致 办公楼 表示 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