1 条题解
-
0
简要题意:给你一个无向图 ,将点集划分为若干个子集,使得对于任意的两个点 ,且 和 不在同一个集合中,满足 和 有连边,求点集最多可以划分为多少个子集,及其方案。
先对无向图的边集 取反得到 ,显然在 中有连边的两个点必须在同一个集合中,所以答案为反图的连通块数和所有连通块的大小。
我们用一个链表维护未删去的点的集合,初始为 ,进行以下操作直到链表为空。
- 从链表中选出任意一个点 加入拓展队列,并从链表中删去,并进行以下操作:
-
- 从拓展队列中选出一个点 ,并删去。
-
- 将所有与 相连的点标记。
-
- 暴力遍历链表将未标记的点加入拓展队列并从链表中删去,然后将有标记的点的标记删去。
- 则反图中 所在的连通块的大小为点加入拓展队列的次数。
因为操作 2 的时间复杂度为 、操作 3 的时间复杂度为 (最多只有 次是只枚举且不从链表中删去)。
所有总时间复杂度为 。
// qwq #include <bits/stdc++.h> using namespace std; constexpr int N=2e5+6; int Vis[N],ans,A[N],ban[N]; int n,m,pr[N],nxt[N]; vector<int>e[N]; queue<int> q; void D(int x){ nxt[pr[x]]=nxt[x]; pr[nxt[x]]=pr[x]; } int main(){ cin>>n>>m; for(int i=1,x,y;i<=m;i++) cin>>x>>y,e[x].push_back(y), e[y].push_back(x); for(int i=1;i<n;i++) pr[i+1]=i,nxt[i]=i+1; nxt[0]=1; for(int i=1;i<=n;i++)if(!Vis[i]){ A[++ans]=1,q.push(i),Vis[i]=1,D(i); while(!q.empty()){ int u=q.front();q.pop();Vis[u]=1; for(int v:e[u])ban[v]=1; for(int j=nxt[0];j;j=nxt[j]) if(!ban[j])D(j),Vis[j]=1,++A[ans],q.push(j); else ban[j]=0; } } sort(A+1,A+ans+1); cout<<ans<<'\n'; for(int i=1;i<=ans;i++) cout<<A[i]<<' '; return 0; }
- 1
信息
- ID
- 2751
- 时间
- 5000ms
- 内存
- 128MiB
- 难度
- 7
- 标签
- 递交数
- 16
- 已通过
- 10
- 上传者