[CF1019C]Sergey's problem[构造]

题意

找出一个集合 \(Q\),使得其中的点两两之间没有连边,且集合中的点可以走不超过两步到达其他所有不在集合中的点。输出任意一组解。

\(n\leq 10^6\)

分析

  • 考虑构造,先从 \(1\)\(n\) 枚举是否存在一个点 \(u\) 还没有被标记过,如果没有就用 \(u\) 去标记能够走到的点,同时将 \(u\) 加入 \(Q\)
    容易证明这样的构造方式能够保证条件2:如果 \(u\) 的入度为 0 那么一定会加入 \(Q\) ,否则如果不在 \(Q\) 中就一定有一个能够到 \(u\) 的点在 \(Q\) 中。

  • 但是顺次枚举就有可能使得“相邻编号大的点能到编号小的点,两个点都在 \(Q\) 中” 这种情况,所以考虑如果出现了这样两个点,我们将编号较大的点保留,能够保证 \(Q\) 中的点在两步以内到达所有点。

  • 总时间复杂度为 \(O(n)\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define go(u) for(int i = head[u], v = e[i].to; i; i=e[i].lst, v=e[i].to)
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define pb push_back
inline int gi() {
    int x = 0,f = 1;
    char ch = getchar();
    while(!isdigit(ch)) {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(isdigit(ch)) {
        x = (x << 3) + (x << 1) + ch - 48;
        ch = getchar();
    }
    return x * f;
}
template <typename T> inline void Max(T &a, T b){if(a < b) a = b;}
template <typename T> inline void Min(T &a, T b){if(a > b) a = b;}
const int N = 1e6 + 7;
int n, m, edc, ans;
int vis[N], mark[N], head[N];
struct edge {
	int lst, to;
	edge(){}edge(int lst, int to):lst(lst), to(to){}
}e[N*2];
void Add(int a, int b) {
	e[++edc] = edge(head[a], b), head[a]=edc;
}
vector<int> res;
int main(){
	n = gi(), m = gi();
	rep(i, 1, m) {
		int x = gi(), y = gi();
		Add(x, y);
	}
	rep(u, 1, n) if(!vis[u]){
		mark[u] = vis[u] = 1;
		go(u) vis[v] = 1;
	}
	for(int u = n; u; --u) if(mark[u]){
		++ans;
		go(u) mark[v] = 0;
	}
	for(int u = 1; u <= n; ++u) if(mark[u]) res.pb(u);
	printf("%d\n", ans); 
	for(int i = 0; i < res.size(); ++i) {
		printf("%d%c",res[i],i == res.size() - 1 ? '\n' : ' ');
	}
	return 0;
}
posted @ 2018-12-12 16:50  fwat  阅读(151)  评论(0编辑  收藏  举报