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