题解:P12737 [POI 2016 R2] 可变方向道路 Vari-directional Streets
作为全洛谷第一个通过的人,我很荣幸。
首先,我们先读清题意。根据样例,我们可知,若一个点在奇数日和偶数日所能走到的点的并集为全集,则该点需被统计。
读完题,我最直接的想法是缩点。因为强连通分量对我们统计答案是无影响的。
接着,我们有可能陷入一个误区,就是答案为所有入度为零的点到出度为零的点的路径上必经的点。这的确都是答案。于是我最开始就写了一个支配树,用于寻找必经点。
支配树的性质是任意一个点 \(u\),从 \(u\) 出发到根节点 \(root\) 的路径上,都是必经点。
然后我们就会 WA 三个点,喜提 \(44pts\)。
怎么回事呢?
我们给出一组小 hack:
3 3
1 2
2 3
1 3
正确输出应是:
3
1 2 3
但是我们发现 \(2\) 并不是必经点。
于是我们就会对原做法进行缝补。
已知,我们求的点是答案的自集,那么怎么找到其它的答案呢?
有一个东西叫做拓扑覆盖。
我们在缩点之后的 DAG 上判断,若该 SCC 可以被所有拓扑序小于它的点到达,也可以到达所有拓扑序大于该 SCC 的点。
我们可以记录后继拓扑序最小值和前驱拓扑序最大值。
对于 \(u = 1,2,3,\dots,cnt\),若有 \(premax[u - 1] \le i\),且 \(sufmin[u + 1] \ge i\),则该 SCC 成立。
注意,这里的左右是只该 SCC 的入边和出边。
这样我们就保证了没有边可以跳过我们所讨论的 SCC。
下面给出代码:
#include<bits/stdc++.h>
using namespace std;
int n, m, dfn[500005], low[500005], dfsid, cnt, num[500005], siz[500005], in[500005], ou[500005], s, t, du[500005], f[500005][20], deep[500005], fa[500005], pos[500005], pre[500005], nxt[500005], premx[500005], sufmn[500005], at[500005];
bool mark[500005], markbelong[500005];
stack<int> st;
vector<int> v[500005], v1[500005], v2[500005], topo, must;
void dfs(int u) {
dfn[u] = low[u] = ++dfsid;
st.push(u);
mark[u] = true;
for (auto i : v[u]) {
if (!dfn[i]) {
dfs(i);
low[u] = min(low[u], low[i]);
} else if (mark[i]) {
low[u] = min(low[u], dfn[i]);
}
}
if (dfn[u] == low[u]) {
++cnt;
while (st.top() != u) {
num[st.top()] = cnt;
mark[st.top()] = false;
st.pop();
}
num[st.top()] = cnt;
mark[st.top()] = false;
st.pop();
}
}
int get_lca(int x, int y) {
if (deep[x] < deep[y]) {
swap(x, y);
}
for (int i = 19; i >= 0; i --) {
if (deep[f[x][i]] >= deep[y]) {
x = f[x][i];
}
}
if (x == y) {
return x;
}
for (int i = 19; i >= 0; i --) {
if (f[x][i] != f[y][i]) {
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
signed main() {
// freopen("P12737_21.in", "r", stdin);
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i ++) {
int x, y;
cin >> x >> y;
v[x].push_back(y);
}
for (int i = 1; i <= n; i ++) {
if (!dfn[i]) {
dfs(i);
}
}
if (cnt == 1) {
cout << n << "\n";
for (int i = 1; i <= n; i ++) {
cout << i << " ";
}
cout << "\n";
return 0;
}
for (int i = 1; i <= n; i ++) {
for (auto j : v[i]) {
if (num[i] != num[j]) {
v1[num[i]].push_back(num[j]);
v2[num[j]].push_back(num[i]);
in[num[j]] ++;
ou[num[i]] ++;
}
}
}
s = cnt + 1;
t = cnt + 2;
for (int i = 1; i <= cnt; i ++) {
if (!in[i]) {
v1[s].push_back(i);
v2[i].push_back(s);
}
}
for (int i = 1; i <= cnt; i ++) {
if (!ou[i]) {
v1[i].push_back(t);
v2[t].push_back(i);
}
}
for (int i = 1; i <= cnt + 2; i ++) {
for (auto j : v1[i]) {
du[j] ++;
}
}
queue<int> q;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
topo.push_back(u);
pos[u] = topo.size();
for (auto i : v1[u]) {
du[i] --;
if (!du[i]) {
q.push(i);
}
}
}
memset(fa, -1, sizeof(fa));
fa[s] = s;
for (int i = 0; i <= 19; i ++) {
f[s][i] = s;
}
bool flag = true;
for (auto i : topo) {
if (flag) {
flag = false;
continue;
}
int father = -1;
for (auto j : v2[i]) {
father = j;
break;
}
for (auto j : v2[i]) {
father = get_lca(father, j);
}
fa[i] = father;
deep[i] = deep[father] + 1;
f[i][0] = father;
for (int j = 1; j <= 19; j ++) {
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
int now = fa[t];
while (now != s) {
must.push_back(now);
markbelong[now] = true;
now = fa[now];
}
for (int i = 1; i <= cnt; i ++) {
at[pos[i] - 1] = i;
pre[i] = 0;
nxt[i] = cnt + 1;
}
for (int i = 1; i <= cnt; i ++) {
for (auto j : v1[i]) {
if (1 <= j && j <= cnt) {
nxt[i] = min(nxt[i], pos[j] - 1);
pre[j] = max(pre[j], pos[i] - 1);
}
}
}
premx[0] = 0;
for (int i = 1; i <= cnt; i ++) {
int u = at[i];
premx[i] = max(premx[i - 1], nxt[u]);
}
sufmn[cnt + 1] = cnt + 1;
for (int i = cnt; i >= 1; i --) {
int u = at[i];
sufmn[i] = min(sufmn[i + 1], pre[u]);
}
for (int i = 1; i <= cnt; i ++) {
int u = at[i];
bool flag1 = (premx[i - 1] <= i), flag2 = (sufmn[i + 1] >= i);
if (flag1 && flag2) {
markbelong[u] = true;
}
}
vector<int> ans;
for (int i = 1; i <= n; i ++) {
if (markbelong[num[i]]) {
ans.push_back(i);
}
}
cout << ans.size() << "\n";
for (auto i : ans) {
cout << i << " ";
}
return 0;
}
本文来自博客园,作者:xuyifei0302,转载请注明原文链接:https://www.cnblogs.com/xuyifei0302/p/21574479

浙公网安备 33010602011771号