CF1062F Upgrading Cities
Solution
首先可以看出,次重要其实就是只有一个点它不能到也不能到它。又因为这是DAG,所以它能到的点与能到它的点是不交的。所以我们可以分开求,以下即考虑可到点集。
那么我们显然有一个 \(\Theta(n^2/\omega)\) 的做法,即是用 bitset 维护可到点集。可以发现没有前途。
考虑到拓扑排序存在以下性质:
任一时刻队列中的点不存在可达性关系。
这个可以证明,因为它连出的节点只有在它弹出队列之后才能加入。
那么如果对于当前节点,如果队列大小 \(\ge 3\),那么这个一定没戏,可以先排除掉这些点。如果队列大小为 \(1\),那么设 \(t\) 为 topo 序在此之前的点的数量,那么可达点数就是 \(n-t\)。对于队列大小为 \(2\) 的情况,假如为 \(u,v\),那么只有 \(u\) 能到达 topo 序在两者之后的所有点才能合法,可以看出如果 \(v\) 连出的点 \(x\),如果存在 \(\text{deg}_x=1\),那么显然不合法,否则可达点数就是 \(n-t-1\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define int long long
#define MAXN 300005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,m,s[MAXN],deg[MAXN];
vector <int> g[MAXN],H[MAXN];
void topo (){
queue <int> q;int tot = 0;
for (Int u = 1;u <= n;++ u) deg[u] = 0;
for (Int u = 1;u <= n;++ u) for (Int v : H[u]) deg[v] ++;
for (Int u = 1;u <= n;++ u) if (!deg[u]) q.push (u);
while (!q.empty()){
int u = q.front();q.pop (),tot ++;
if (q.size() == 0) s[u] += n - tot;
else if (q.size() == 1){
int v = q.front();bool flg = 1;
for (Int x : H[v]) if (deg[x] == 1){flg = 0;break;}
if (flg) s[u] += n - tot - 1;
else s[u] -= 1e9;
}
else s[u] -= 1e9;
for (Int v : H[u]) if (!-- deg[v]) q.push (v);
}
}
signed main(){
read (n,m);
for (Int i = 1,u,v;i <= m;++ i) read (u,v),g[u].push_back (v);
for (Int u = 1;u <= n;++ u) H[u] = g[u];
topo ();
for (Int u = 1;u <= n;++ u) H[u].clear ();
for (Int u = 1;u <= n;++ u) for (Int v : g[u]) H[v].push_back (u);
topo ();
int ans = 0;
for (Int u = 1;u <= n;++ u) if (s[u] >= n - 2) ans ++;
write (ans),putchar ('\n');
return 0;
}