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;
}
posted @ 2020-08-28 21:43  JiaZP  阅读(67)  评论(0编辑  收藏  举报