[BZOJ 1098] [POI2007] 办公楼biu 【链表优化BFS】

题目链接:BZOJ - 1098

题目分析

只有两个点之间有边的时候它们才能在不同的楼内,那么就是说如果两个点之间没有边它们就一定在同一座楼内。

那么要求的就是求原图的补图的连通块。

然而原图的补图的边数是 n^2 级别的,非常庞大,我们不能直接把补图求出来。

可以使用一种用链表优化BFS的做法,开始时将所有的点加到一个链表里。

每次找一个连通块的时候BFS,在链表中取出一个点,在链表中删除,加入队列,然后每次取出队首元素x,枚举x的每一条边,将边的终点y从链表中删去,加到一个临时的链表中存储。

这样枚举完 x 的所有边之后,原链表里剩余的点就是与 x 没有边的,这些点在补图里与 x 就是有边的,将这些点加入队列。

然后用临时的链表替代原链表,原链表中就是剩下的点了。

这样BFS几次就可以求出所有连通块了。

每一个点都只被从链表中删去1次,每条边都只遍历了一次,总的时间复杂度是 O(n + m)。

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <queue>

using namespace std;

const int MaxN = 100000 + 5, MaxM = 2000000 + 5;

inline void Read(int &Num)
{
	char c; c = getchar();
	while (c < ‘0‘ || c > ‘9‘) c = getchar();
	Num = c - ‘0‘; c = getchar();
	while (c >= ‘0‘ && c <= ‘9‘)
	{
		Num = Num * 10 + c - ‘0‘;
		c = getchar();
	}
}

int n, m, Top, R1, R2, Sum;
int Ans[MaxN], Last[MaxN], Next[MaxN];

bool InList[MaxN];

struct Edge
{
	int v;
	Edge *Next;
} E[MaxM * 2], *P = E, *Point[MaxN];

inline void AddEdge(int x, int y)
{
	++P; P -> v = y;
	P -> Next = Point[x]; Point[x] = P;
}

queue<int> Q;

inline void Add(int x, int y)
{
	Next[y] = Next[x];
	if (Next[x]) Last[Next[x]] = y;
	Next[x] = y;
	Last[y] = x;
}

inline void Delete(int x)
{
	if (Last[x]) Next[Last[x]] = Next[x];
	if (Next[x]) Last[Next[x]] = Last[x];
}

void BFS()
{
	while (!Q.empty()) Q.pop();
	Q.push(Next[R1]);
	InList[Next[R1]] = false;
	Delete(Next[R1]);
	int x, y;
	++Top;
	while (!Q.empty())
	{
		x = Q.front();
		++Sum;
		++Ans[Top];
		Q.pop();
		R2 = n + 2;
		Last[R2] = Next[R2] = 0;
		for (Edge *j = Point[x]; j; j = j -> Next)
		{
			y = j -> v;
			if (!InList[y]) continue;
			Delete(y);
			Add(R2, y);
		}
		for (int i = Next[R1]; i; i = Next[i])
		{
			Q.push(i);
			InList[i] = false;
		}
		Next[R1] = Next[R2];
		Last[Next[R1]] = R1;
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	int a, b;
	for (int i = 1; i <= m; ++i)
	{
		Read(a); Read(b);
		AddEdge(a, b);
		AddEdge(b, a);
	}
	Top = 0; Sum = 0;
	R1 = n + 1;
	for (int i = 1; i <= n; ++i) Add(R1, i);
	for (int i = 1; i <= n; ++i) InList[i] = true;
	while (Sum < n) BFS();
	printf("%d\n", Top);
	sort(Ans + 1, Ans + Top + 1);
	for (int i = 1; i <= Top; ++i) printf("%d ", Ans[i]);
	return 0;
}

  

时间: 2024-10-13 12:37:26

[BZOJ 1098] [POI2007] 办公楼biu 【链表优化BFS】的相关文章

BZOJ 1098 [POI2007]办公楼biu 链表

description Bytel is a mobile telephony potentate. Each employee has been issued a company phone, the memory ofwhich holds the numbers of some of his co-workers (all of them have his number in their phones as well). Due to dynamic growth of their ope

bzoj 1098 [POI2007]办公楼biu bfs+补图+双向链表

[POI2007]办公楼biu Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 1543  Solved: 743[Submit][Status][Discuss] Description FGD开办了一家电话公司.他雇用了N个职员,给了每个职员一部手机.每个职员的手机里都存储有一些同事的电话号码.由于FGD的公司规模不断扩大,旧的办公楼已经显得十分狭窄,FGD决定将公司迁至一些新的办公楼.FGD希望职员被安置在尽量多的办公楼当中,这样对于每个职员

bzoj 1098 [POI2007] 办公楼 biu

# 解题思路 画画图可以发现,只要是两个点之间没有相互连边,那么就必须将这两个人安排到同一个办公楼内,如图所示: 那,我们可以建立补图,就是先建一张完全图,然后把题目中给出的边都删掉,这就是一张补图,显然补图中相互连边的点就放在同一栋办公楼内. 我们可以用并查集来完成,但是数据范围显然不允许用这样的方法,建图的复杂度是 $N^2$ 的.所以考虑另一种方法: 将原图建立好,在原图中,从一个点开始,把这个点所能够直接到达的点标记出来,这些点是不可以放在一起的.然后将这些点删除. 之后对每一个点都进行

BZOJ 1098[POI2007]办公楼

题面: 1098: [POI2007]办公楼biu Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 1371  Solved: 641[Submit][Status][Discuss] Description FGD开办了一家电话公司.他雇用了N个职员,给了每个职员一部手机.每个职员的手机里都存储有一些同事的电话号码.由于FGD的公司规模不断扩大,旧的办公楼已经显得十分狭窄,FGD决定将公司迁至一些新的办公楼.FGD希望职员被安置在尽量多的办公楼当

BZOJ1098: [POI2007]办公楼biu

1098: [POI2007]办公楼biu Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 777  Solved: 326[Submit][Status] Description FGD开办了一家电话公司.他雇用了N个职员,给了每个职员一部手机.每个职员的手机里都存储有一些同事的电话号码.由于FGD的公司规模不断扩大,旧的办公楼已经显得十分狭窄,FGD决定将公司迁至一些新的办公楼. FGD希望职员被安置在尽量多的办公楼当中,这样对于每个职员来说都

【BZOJ1098】[POI2007]办公楼biu

题目一开始看以为和强联通分量有关,后来发现是无向边,其实就是求原图的补图的联通块个数和大小.学习了黄学长的代码,利用链表来优化,其实就是枚举每一个人,然后把和他不相连的人都删去放进同一个联通块里,利用bfs来实现.——by VANE #include<bits/stdc++.h> using namespace std; const int N=100005; const int M=4000005; struct edge{int to,next;}e[M]; int a[N],q[N];

BZOJ1098 办公楼biu (BFS+链表优化)

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1098分析:见注释. 1 // 补图连通块 bfs + 链表优化 2 #include <cstdio> 3 #include <queue> 4 #include <vector> 5 #include <algorithm> 6 using namespace std; 7 8 struct Edge { 9 int to, nxt; 10 }; 11

BZOJ 1855 股票交易(单调队列优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1855 题意:最近lxhgww又迷上了投资股票, 通过一段时间的观察和学习,他总结出了股票行情的一些规律. 通过一段时间的观察,lxhgww预测到了未来T天内某只股票的走势,第i天的股票买入价为每股APi,第i天的股票卖出价为每股BPi(数据保证对于每 个i,都有APi>=BPi),但是每天不能无限制地交易,于是股票交易所规定第i天的一次买入至多只能购买ASi股,一次卖出至多只能卖出BS

HDU4941Magical Forest (二分+链表优化)

Magical Forest Time Limit: 24000/12000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 510 Accepted Submission(s): 239 Problem Description There is a forest can be seen as N * M grid. In this forest, there is some ma