[bzoj1098][POI2007]办公楼biu——复杂度分析+广搜

题目大意:

FGD开办了一家电话公司。他雇用了N个职员,给了每个职员一部手机。每个职员的手机里都存储有一些同事的电话号码。由于FGD的公司规模不断扩大,旧的办公楼已经显得十分狭窄,FGD决定将公司迁至一些新的办公楼。FGD希望职员被安置在尽量多的办公楼当中,这样对于每个职员来说都会有一个相对更好的工作环境。但是,为了联系方便起见,如果两个职员被安置在两个不同的办公楼之内,他们必须拥有彼此的电话号码。

思路:

直接考虑哪些人必须要在一个办公室的话,题目的问题就简化成了在这个图的补图内寻找连通块的个数。
暴力地寻找连通块复杂度是 n 2 n^2 n2的,但是冷静分析一下其实并不是这一回事,如果我们每一次广搜到一个点并且将它从未确定归属的集合内删除,以后不再便历这个点,那么一个点被便历的次数将=(并不能将它加入某个连通块的便历的次数+最后一次刚好加入某个连通块的那一次便历)。
不难发现复杂度的瓶颈主要在前面那一个部分,同时也可以发现只有当这条边存在于图中的时候,这次便历才是没有用处的,所以这样的无用的便历次数的总和最多为m,所以不难得出最终的复杂度是 Θ ( n + m ) \Theta(n+m) Θ(n+m)

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
typedef long long ll;

using namespace std;

void File(){
	freopen("bzoj1098.in","r",stdin);
	freopen("bzoj1098.out","w",stdout);
}

template<typename T>void read(T &_){
	T __=0,mul=1; char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')mul=-1;
		ch=getchar();
	}
	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
	_=__*mul;
}

const int maxn=1e5+10;
const int maxm=2e6+10;
int n,m,q,sz,tmp[maxn],tot_tmp,ans,lst[maxn];
vector<int>to[maxn];
set<int>st;
set<int>::iterator it;
queue<int>qu;

void init(){
	read(n); read(m);;
	int u,v;
	REP(i,1,m){
		read(u); read(v);
		to[u].push_back(v);
		to[v].push_back(u);
	}
	REP(i,1,n)sort(to[i].begin(),to[i].end());
}

bool judge(int u,int v){
	if(to[u].size()>to[v].size())swap(u,v);
	int p=lower_bound(to[u].begin(),to[u].end(),v)-to[u].begin();
	if(p==(int)to[u].size() || to[u][p]!=v)return true;
	return false;
}

void work(){
	REP(i,1,n)st.insert(i);
	while(!st.empty()){
		++ans;
		int u=*st.begin();
		st.erase(u);
		qu.push(u);
		while(!qu.empty()){
			u=qu.front(); qu.pop();
			++lst[ans];
			tot_tmp=0;
			for(it=st.begin();it!=st.end();++it){
				int v=*it;
				if(judge(u,v))tmp[++tot_tmp]=v;
			}
			REP(i,1,tot_tmp){
				st.erase(tmp[i]);
				qu.push(tmp[i]);
			}
		} 
	}
	printf("%d\n",ans);
	sort(lst+1,lst+ans+1);
	REP(i,1,ans)printf("%d ",lst[i]);
}

int main(){
	File();
	init();
	work();
	//cerr<<(double)clock()/CLOCKS_PER_SEC<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值