• Codeforces 1019C Sergey's problem 构造


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

    题目传送门 - CF1019C

    题意

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

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

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

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

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

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

      $n,mleq 10^6$

    题解

      我们考虑以下构造方案:

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

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

      3. 如果 $V^prime eq 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 eq emptyset$ ,则返回第 1. 步。否则输出答案集合 $S$ 。

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

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

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

        ③ 对于任意满足 $xin V,x otin v$ 的节点 $x$ ,它只可能在第 2 步的时候被删除,那么,显然有一个能一步到达 $x$ 的节点 $y$ 在集合 $v$ 中。又根据 ② ,如果 $y otin S$ ,有一步到 $y$ 的节点,则 $x$ 可以花两步到达;否则,$yin 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;
    }
    

      

  • 相关阅读:
    标准C语言(9)
    标准C语言(8)
    标准C语言(7)
    标准C语言(6)
    标准C语言(5)
    标准C语言(4)
    标准C语言(3)
    标准C语言(1)
    Linux基础
    Kafka 学习笔记之 Kafka0.11之console-producer/console-consumer
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF1019C.html
Copyright © 2020-2023  润新知