【CF1019C】Sergey's problem

题目

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

  1. \(S\) 中任意两个点的距离不小于 \(2\)
  2. 对于任意不在 \(S\) 中的点 \(x\),存在一个 \(S\) 中的点 \(y\) 使得 \(y\)\(x\) 的距离不大于 \(2\)

输出任意一中解。
\(n,m\leq 10^6\)

思路

直接给解法:
从小到大枚举所有点,如果这个点没有被标记,那么把这个点能一步到达的所有编号大于它的点标记。
再从大到小枚举所有点,如果这个点没有被标记,那么把这个点能一步到达的所有编号小于它的点标记。
最后没有被标记的点就是一个合法解。
证明:
记第一次被标记的点集为 \(S_1\),第二次被标记的点集为 \(S_2\),最后没有被标记的点集为 \(S\)
\(S\) 中任意两个点距离肯定是不小于 \(2\) 的。否则被到达的点一定会被标记上。
\(S_2\) 中每一个点都可以从 \(S\) 其中一个点一步到达,\(S_1\) 中每一个点都可以从 \(S\)\(S_2\) 其中一个点一步到达,也就是 \(S_1\cup 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<<"\n";
	for (int i=1;i<=n;i++)
		if (!vis[i]) cout<<i<<" ";
	return 0;
}
posted @ 2021-09-09 17:24  stoorz  阅读(45)  评论(0编辑  收藏  举报