CF1916F Group Division 题解
upd 2024.11.20 “(否则选出这个点后 就不连通了)” 改为
upd 2024.10.10 “且选出的点不是剩余点集 的割点” 改为
-
题意
给定一个无向图 ,点数为 ,边数为 ,保证 是一个点双连通分量且无重边。
请将 划分为两个无交集的点集 , 满足 ,,且 , 的导出子图均连通。
保证有解。
-
思路
- 因为 $ 1 \le n_1 , n_2 \le 2000 1 \le m \le 5000 $ 所以可以考虑一个点一个点地选取和划分,每一次选取花费时间 或更少。
- 因此可以定义 为空集,所有点都在 中,之后,每次从 取出 个点放入 ,执行 次。
- 然后考虑怎样的点可以被选取,易得,只要保证新选出的点与 连通(题目要求保证两个子图均连通)且选出的点不是剩余点集 的割点(否则选出这个点后 就不连通了)。
- 于是,就可以用 Tarjan 求割点解决了。
-
注意
- 每次求割点之前一定要清空相关数组。
- 注意多测要清空存图的
vector
。 - 注意取出的点要在 删除。
- 开始时先取任意一点放入 并注意要在 中删点。
-
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112//#include<bits/stdc++.h>
using namespace std;
const int maxn=20000;
vector<int>G[maxn+100];
vector<int>GG[maxn+100];
int dfn[maxn+100],low[maxn+100],tot;
int n,m;
int cut[maxn+100];
void del_point(int p){
for(auto po:G[p]){
for(int i=0;i<G[po].size();i++){
if(G[po][i]==p){
G[po].erase(G[po].begin()+i);
}
}
}
G[p].clear();
}
void Tarjan(int u,int root){
low[u]=dfn[u]=++tot;
int cnt=0;
for(auto v:G[u]){
if(!dfn[v]){
Tarjan(v,root);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])cnt++;
}
else{
low[u]=min(low[u],dfn[v]);
}
}
if(u==root){
if(cnt>1)cut[u]=1;
}
else
if(cnt>0)cut[u]=1;
}
void find_cut(){
for(int i=1;i<=n;i++){
if(dfn[i])continue;
Tarjan(i,i);
}
}
bool ans[maxn+100];
void init(bool x){
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(cut,0,sizeof(cut));
tot=0;
if(x){
memset(ans,0,sizeof(ans));
memset(G,0,sizeof(G));
memset(GG,0,sizeof(GG));
}
}
int CASEmain(){
init(true);
int n1,n2;
cin>>n1>>n2>>m;
n=n1+n2;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
GG[u].push_back(v);
GG[v].push_back(u);
}
cout<<1<<" ";
ans[1]=1;
del_point(1);
for(int i=1;i<n1;i++){
init(false);
find_cut();
set<int>ps;
for(int j=1;j<=n;j++){
if(ans[j]){
for(auto k:GG[j]){
ps.insert(k);
}
}
}
for(auto j:ps){
if(!cut[j]&&G[j].size()!=0){
cout<<j<<" ";
ans[j]=1;
del_point(j);
break;
}
}
}
cout<<endl;
for(int i=1;i<=n;i++){
if(!ans[i])
cout<<i<<" ";
}
cout<<endl;
return 0;
}
int main(){
int t;
cin>>t;
for(int i=1;i<=t;i++){
CASEmain();
}
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 shicj's blog!
评论