【CF1019C】Sergey's problem

题目

题目链接:https://codeforces.com/problemset/problem/1019/C
给定一张无自环的有向图,选出一个点集 (S),使得:

  1. (S) 中任意两个点的距离不小于 (2)
  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;
}
原文地址:https://www.cnblogs.com/stoorz/p/15247984.html