ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P3452 [POI2007]BIU-Offices & ZLOJ 练习56 D

2022-08-04 08:02:25  阅读:159  来源: 互联网

标签:int POI2007 56 链表 P3452 补图 集合 考虑 删点


written on 2022-08-01

第一道补上来的链表题,纪念一下。

链表一般在需要高效地删除或添加元素时使用。由于修改操作是 \(O(1)\) 的,正是由于链表这一区别与普通数组的最大优点,一般在要删点的题目中偶有遇到。

具体到这题,可以先尝试暴力的思路。考虑建出原图的补图,用并查集维护集合关系,最后统计集合个数即可。由于点数过多,补图很大,因此需要优化。

一开始在思考如何优化这一建补图的过程,最终无果。正确的思考方式,需要考虑到一个点,也就是说如果两个点已经在同一个集合中,那么就不用考虑他们之间的补图连边。由此我们可以想见,对于一个已经在某一集合中的点,可以直接从待选数组中删去,使得后续的连边过程不再考虑这个点。这点可以画图模拟然后感性理解一下。

由于涉及到从数组中删点的操作,所以我们可以考虑用链表维护这一高效删点的过程。时间复杂度 \(O(n+m)\),详见代码。

#include<bits/stdc++.h>
#define N 100005
#define M 2000005
using namespace std;
int n,m;
int tot,ver[M<<1],nxt[M<<1],head[N];
void add_E(int x,int y){ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;}
struct F{int l,r;}Lk[N];
void Del(int x){Lk[Lk[x].l].r=Lk[x].r,Lk[Lk[x].r].l=Lk[x].l;}
queue<int> q;
bool mark[N];
int ans[N];
int main()
{
	scanf("%d%d",&n,&m);
	while(m--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add_E(x,y),add_E(y,x);
	}
	for(int i=1;i<=n;i++)
	{
		if(i!=n) Lk[i].r=i+1;
		Lk[i].l=i-1;
	}
	Lk[0].r=1,Lk[n].r=-1;
	int cur=0;
	while(Lk[0].r!=-1)
	{
		ans[++cur]++;
		q.push(Lk[0].r);
		Del(Lk[0].r);
		while(q.size())
		{
			int x=q.front();
			q.pop();
			for(int i=head[x];i;i=nxt[i])
			{
				int y=ver[i];
				mark[y]=1;
			}
			for(int i=Lk[0].r;Lk[i].r;i=Lk[i].r) if(!mark[i]) ans[cur]++,q.push(i),Del(i);
			for(int i=Lk[0].r;Lk[i].r;i=Lk[i].r) mark[i]=0;
		}
	}
	printf("%d\n",cur);
	sort(ans+1,ans+1+cur);
	for(int i=1;i<=cur;i++) printf("%d ",ans[i]);
}

莫名其妙的一遍 A。。

标签:int,POI2007,56,链表,P3452,补图,集合,考虑,删点
来源: https://www.cnblogs.com/Freshair-qprt/p/16549356.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有