题目
题目链接:https://codeforces.com/problemset/problem/1019/C
给定一张无自环的有向图,选出一个点集 (S),使得:
- (S) 中任意两个点的距离不小于 (2)。
- 对于任意不在 (S) 中的点 (x),存在一个 (S) 中的点 (y) 使得 (y) 到 (x) 的距离不大于 (2)。
输出任意一中解。
(n,mleq 10^6)。
思路
直接给解法:
从小到大枚举所有点,如果这个点没有被标记,那么把这个点能一步到达的所有编号大于它的点标记。
再从大到小枚举所有点,如果这个点没有被标记,那么把这个点能一步到达的所有编号小于它的点标记。
最后没有被标记的点就是一个合法解。
证明:
记第一次被标记的点集为 (S_1),第二次被标记的点集为 (S_2),最后没有被标记的点集为 (S)。
(S) 中任意两个点距离肯定是不小于 (2) 的。否则被到达的点一定会被标记上。
而 (S_2) 中每一个点都可以从 (S) 其中一个点一步到达,(S_1) 中每一个点都可以从 (S) 或 (S_2) 其中一个点一步到达,也就是 (S_1cup S_2) 中所有点都可以通过 (S) 两步内到达。
时间复杂度 (O(n+m))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1000010;
int n,m,tot,head[N];
bool vis[N];
struct edge
{
int next,to;
}e[N];
void add(int from,int to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for (int i=1,x,y;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
}
for (int i=1;i<=n;i++)
if (!vis[i])
for (int j=head[i];~j;j=e[j].next)
if (e[j].to>i) vis[e[j].to]=1;
for (int i=n;i>=1;i--)
if (!vis[i])
for (int j=head[i];~j;j=e[j].next)
if (e[j].to<i) vis[e[j].to]=1;
tot=0;
for (int i=1;i<=n;i++)
if (!vis[i]) tot++;
cout<<tot<<"
";
for (int i=1;i<=n;i++)
if (!vis[i]) cout<<i<<" ";
return 0;
}