其他分享
首页 > 其他分享> > P3452 [POI2007]BIU-Offices & ZLOJ 练习56 D

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

作者:互联网

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