Codeforces 1019C Sergey's problem 构造

原文链接https://www.cnblogs.com/zhouzhendong/p/CF1019C.html

题目传送门 - CF1019C

题意

  给定一个有 $n$ 个节点 、 $m$ 条边的有向图,没有自环,但是可能存在环。

  现在要求选出一个点集满足一下条件。

  设原来的所有点构成的点集为 $V$ ,选出的点集为 $S$,则:

  1. 对于所有满足 $x,y\in S$ 的点 $x,y$ ,有向边 $(x,y)$ 不存在。

  2. 对于所有满足 $y\in V$ 的点,都可以找到一个点 $x(x\in S)$,满足从点 $x$ 开始走到 $y$ 的最少经过边数不超过 2 。

  首先输出你选出的点数,然后按照编号从小到大输出你选的点。

  $n,m\leq 10^6$

题解

  我们考虑以下构造方案:

  1. 记当前的图为 $G(V,E)$ 。

  2. 选择一个节点 $A\in V$ ,从 $G$ 中删除节点 $A$ ,以及从 $A$ 出发的有向边连向的所有节点,得到新图 $G^\prime$ 。

  3. 如果 $V^\prime \neq \emptyset $ ,则返回第 1. 步。否则到第 4 步。

  4. 记之前选出的所有节点 $A$ 构成的集合为 $v$ ,取 $v$ 和 原图 $G$ 中只与 $v$ 中的点有关边集 $e$ ,构成新图 $g(v,e)$ 。容易得知,$g$ 是一个有向无环图。

  5. 记当前的图为 $g(v,e)$ 。

  6. 取一个入度为 $0$ 的节点 $a$ ,并将该节点加入答案集合 $S$,删除 $a$ 以及在 $g$ 中 $a$ 能一步走到的所有点。设得到的新图的点集为 $v^\prime$ 。

  7. 如果 $v^\prime \neq \emptyset$ ,则返回第 1. 步。否则输出答案集合 $S$ 。

  现在简单的说明一下这样做的正确性:

    ① 首先,显然任意两个属于答案集合点不能一步到达。

    ② 对于任意满足 $x\in v,x\notin S$ 的节点 $x$ ,它只可能在第 6 步被删除,那么,显然有一个能一步达到 $x$ 的节点被记入答案。

    ③ 对于任意满足 $x\in V,x\notin v$ 的节点 $x$ ,它只可能在第 2 步的时候被删除,那么,显然有一个能一步到达 $x$ 的节点 $y$ 在集合 $v$ 中。又根据 ② ,如果 $y\notin S$ ,有一步到 $y$ 的节点,则 $x$ 可以花两步到达;否则,$y\in S$ ,$x$ 可以由 $y$ 一步到达。

    ④ 由于属于答案集合的节点显然可以在 2 步以内到达,再根据 ②③ ,上述做法的正确性显然。

  接下来就只差一个方便的实现方法了,详见代码。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1000005;
int read(){
	int x=0;
	char ch=getchar();
	while (!isdigit(ch))
		ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+ch-48,ch=getchar();
	return x;
}
void write(int v){
	int k=v/10;
	if (v>9)
		write(k);
	putchar('0'+(v-k*10));
}
struct Gragh{
	static const int M=1000005;
	int cnt,y[M],nxt[M],fst[N];
	void clear(){
		cnt=0;
		memset(fst,0,sizeof fst);
	}
	void add(int a,int b){
		y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
	}
}g;
int n,m;
int vis[N],ans[N],anscnt=0;
int main(){
	n=read(),m=read();
	g.clear();
	for (int i=1;i<=m;i++){
		int a=read(),b=read();
		g.add(a,b);
	}
	for (int i=1;i<=n;i++)
		if (!vis[i]){
			vis[i]=-2;
			for (int j=g.fst[i];j;j=g.nxt[j])
				vis[g.y[j]]=min(vis[g.y[j]],-1);
		}
	for (int i=n;i>=1;i--)
		if (vis[i]==-2){
			ans[++anscnt]=i;
			for (int j=g.fst[i];j;j=g.nxt[j])
				vis[g.y[j]]=-1;
		}
	write(anscnt),puts("");
	for (int i=anscnt;i>=1;i--)
		write(ans[i]),putchar(' ');
	return 0;
}

  

posted @ 2018-08-12 22:53  zzd233  阅读(480)  评论(0编辑  收藏  举报