CF1062F Upgrading Cities
题意可以转化为:求能到达的点数 + 到达它的点数 \(\le n - 2\) 的点数。
发现两个问题类似,正反都做一遍即可。现在我们的任务是求它能到达的点数。
因为 DAG, 考虑拓扑排序。发现如果可能有贡献的话,这个点被取出以后的那个瞬间,队列要么为空,要么只有一个点。如果为空,他肯定能到达剩下的所有点;如果只有一个点,那么我们还要求剩下的(没有入过队的)点都是当前点能到达的。经过仔细思考,我们发现不合法当且仅当队列里剩下的那个点 \(y\) 有它自己的一个“块”,而 \(y\) 有自己的“块”就一定有一条出边,对应的那个点只有这一个入度。(这是充要条件,否则会出环)因此特判即可。
inline void che(int x, int y) {
for (register int i = head[y]; i; i = e[i].nxt) {
int to = e[i].to; if (ind[to] == 1) return vis[x] = true, void();
}
cnt[x] += n - nwtot;
}
inline void topo() {
front = rear = 0;
nwtot = 0;
for (register int i = 1; i <= n; ++i) if (!ind[i]) que[++rear] = i, ++nwtot;
while (front < rear) {
int cur = que[++front];
if (front == rear) cnt[cur] += n - nwtot;
else if (rear - front == 1) che(cur, que[front + 1]);
for (register int i = head[cur]; i; i = e[i].nxt) {
int to = e[i].to; --ind[to];
if (!ind[to]) que[++rear] = to, ++nwtot;
}
}
}
int main() {
read(n); read(m);
for (register int i = 1; i <= m; ++i) {
int u, v; read(u), read(v);
U[i] = u, V[i] = v;
addedge(u, v);
}
topo();
memset(head, 0, sizeof(head));
ecnt = 0;
memset(ind, 0, sizeof(ind));
for (register int i = 1; i <= m; ++i) addedge(V[i], U[i]);
topo();
int ans = 0;
for (register int i = 1; i <= n; ++i) if (!vis[i] && cnt[i] >= n - 2) ++ans;
printf("%d\n", ans);
return 0;
}