建议访问原文出处,获得更佳浏览体验。
原文出处:https://hyp1231.github.io/2018/07/08/20180708-codem/
题意
个点的图,依次添加给出的
条有向边。
每次添加后,如果图中有环,输出
;否则输出
。
输入
第一行两个正整数
和
。
之后
行,每行两个正整数
表示加密后的边,保证
记上一个回答为
,实际的边为
则有
最开始的 为
链接
题解
如果加了某条边后,图中出现环,那么这条边及以后的
应全为
。
因此题意即为找到
序列的
和
的分界线。
考虑二分答案,每次判断
的边构成的图是否为 DAG。
如果对
的边建成的图可以做拓扑排序,则是 DAG;否则说明必有环。
由于拓扑排序时间复杂度 ,总时间复杂度 。
Hint: 我们可以在读入数据的时候预处理出解密后的 :
如果上一个 是 ,则 ;
否则, 。 同理。
代码
#include <cstdio>
#include <cstring>
#include <queue>
const int N = 100010;
struct E {
int u, v;
E(int u = 0, int v = 0) : u(u), v(v) {}
} pre_0[N << 1], pre_1[N << 1];
// 如果上一个 ans 是 0,实际的边为 pre_0[i];否则是 pre_1[i]
int n, m;
bool vis[N];
int degree[N]; // 节点度数
int e_id, head[N]; // e_id 记录边数
struct Edge {
int u, v, next;
Edge(int u = 0, int v = 0, int next = 0) :u(u), v(v), next(next) {}
} e[N << 1];
inline void addedge(int a, int b) {
e[++e_id].v = b;
e[e_id].u = a;
e[e_id].next = head[a];
head[a] = e_id;
}
void build(int id) {
memset(head, 0, sizeof(head));
e_id = 0;
addedge(pre_0[0].u, pre_0[0].v); // 初始 ans 为 0
for(int i = 1; i <= id; ++i) { // 假设前 id 条边构成 DAG
int u = pre_1[i].u, v = pre_1[i].v;
addedge(u, v);
}
}
bool topo(int id) {
build(id); // 建图
int cnt = 0;
memset(vis, 0, sizeof(vis));
memset(degree, 0, sizeof(degree));
++degree[pre_0[0].v];
for (int i = 1; i <= id; ++i)
++degree[pre_1[i].v];
std::queue<int> que;
for (int i = 1; i <= n; ++i)
if (degree[i] == 0) {
que.push(i);
vis[i] = true;
++cnt;
}
while (!que.empty()) {
int u = que.front(); que.pop();
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
if (vis[v])continue;
--degree[v];
if (degree[v] == 0) {
que.push(v);
vis[v] = true;
++cnt;
}
}
}
return cnt == n;
} // 如果拓扑有序,返回 true,O(n)
int solve(int l, int r) {
if (l == r) return l;
int mid = (l + r + 1) >> 1;
if (topo(mid)) return solve(mid, r);
else return solve(l, mid - 1);
} // 二分,返回最大的可以构成 DAG 的边数,即 [1, id] 可以构成 DAG,但 [1, id + 1] 不可以
int main() {
scanf("%d%d", &n, &m);
int u, v;
for (int i = 0; i < m; ++i) {
scanf("%d%d", &u, &v);
--u; --v;
if (u == 0) u = n;
if (v == 0) v = n; // 取模
pre_0[i].u = u;
pre_0[i].v = v;
}
for (int i = 1; i < m; ++i) {
int u = pre_0[i].u - 1, v = pre_0[i].v - 1;
if (u == 0) u = n;
if (v == 0) v = n;
pre_1[i].u = u;
pre_1[i].v = v;
}
int k = solve(0, m - 1);
for (int i = 0; i < m; ++i) {
printf("%d\n", (i <= k));
}
return 0;
}