题意
找出一个集合 (Q),使得其中的点两两之间没有连边,且集合中的点可以走不超过两步到达其他所有不在集合中的点。输出任意一组解。
(nleq 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
", ans);
for(int i = 0; i < res.size(); ++i) {
printf("%d%c",res[i],i == res.size() - 1 ? '
' : ' ');
}
return 0;
}