题意
给定一个n个点m条边的有向图,要求选出一个点集S,满足S中的任意两点间没有边相连,且对于任何一个不属于S的点y,必然存在S中的某个点x满足
d(x,y)≤2
d
(
x
,
y
)
≤
2
,其中
d(x,y)
d
(
x
,
y
)
表示x到y的最短路。
n,m≤106
n
,
m
≤
10
6
,题目保证有解。
分析
具体一点的题解可以看这篇文章。
大概做法就是,对于当前图G中的某个点w,我们把w和所有满足
d(w,u)=1
d
(
w
,
u
)
=
1
的点u都删掉,就得到了G的一个子图G’,然后递归下去求答案。
当我们得到G’的答案S’时,若S’中某个点v满足
d(v,w)=1
d
(
v
,
w
)
=
1
则S’就是G的答案,否则我们就把S’中的点加上w作为G的答案。
这样做显然是对的。
实现起来有比较简洁的方法,具体可以看代码。
时间复杂度
O(n+m)
O
(
n
+
m
)
。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define pb push_back
const int N=1000005;
int n,m,vis[N];
bool used[N];
std::vector<int> vec[N];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
vec[x].pb(y);
}
for (int i=1;i<=n;i++)
if (!vis[i])
{
vis[i]=i;
for (int j=0;j<vec[i].size();j++)
if (!vis[vec[i][j]]) vis[vec[i][j]]=i;
used[i]=1;
}
for (int i=n;i>=1;i--)
if (used[i])
{
for (int j=0;j<vec[i].size();j++) used[vec[i][j]]=0;
}
int ans=0;
for (int i=1;i<=n;i++) if (used[i]) ans++;
printf("%d\n",ans);
for (int i=1;i<=n;i++) if (used[i]) printf("%d ",i);
return 0;
return 0;
}