嘘~ 正在从服务器偷取页面 . . .

UOJ134 【UR #9】App 管理器 题解


UOJ134 【UR #9】App 管理器 题解

题目链接:#134. 【UR #9】App 管理器

题意

iOS 用户和 Android 用户一定对 App 管理器不会陌生。

“啊啊啊!手机空间又不够了” 买了低端机的 cxy 欲哭无泪。

cxy 常有的文件格式一共有 $n$ 种,分别编号为 $1, \cdots, n$。 cxy 有 $m$ 个 App,分别编号为 $1, \cdots, m$。其中 $i$ 号 App 可以把 $a_i$ 号文件格式转换成 $b_i$ 号文件格式,或者把 $b_i$ 号文件格式转换成 $a_i$ 号文件格式。

App 管理器最近推出了一个新功能:卸载一半!于是 cxy 决定把每个 App 都卸载一半。

对于 $i$ 号 App,你可以决定你卸载哪一半:

  1. 把 $a_i$ 号文件格式转换成 $b_i$ 号文件格式。
  2. 把 $b_i$ 号文件格式转换成 $a_i$ 号文件格式。

cxy 希望,把所有 App 都卸载一半之后并不影响使用。即,对于任意两个文件格式 $i, j$,仍能通过一个或多个 App 把 $i$ 号文件格式直接或间接转换成 $j$ 号文件格式。

现在 cxy 已经把部分 App 卸载一半了,请你给出一组卸载剩下的 App 的方案满足条件。

省流:一个混合图,把里面的无向边都定向,使得定向之后的图强连通,求一个方案。

输入格式

第一行两个整数 $n, m$,表示文件格式的数量和 App 的数量。保证 $n \geq 1, m \geq 0$。

接下来 $m$ 行,每行三个整数 $a_i, b_i, t_i$,表示一个 App。

如果 $t_i = 0$ 则表示这个 App 还没被卸载一半,如果 $t_i = 1$ 则表示这个 App 已经被卸载一半,现在只能把 $a_i$ 号格式转换为 $b_i$ 号格式。

保证 $1 \leq a_i, b_i \leq n$ 且 $a_i \neq b_i$。

输出格式

输出 $m$ 行,每行一个整数表示卸载的是哪一半。

如果这个 App 还没被卸载一半,输出 0 表示保留 “把 $a_i$ 号文件格式转换成 $b_i$ 号文件格式” 的这一半,否则表示保留 “把 $b_i$ 号文件格式转换成 $a_i$ 号文件格式” 的这一半。

如果这个 App 已经被卸载一半,那么直接输出 0。

保证至少存在一组方案。

数据范围

$n,m \le 5000$ 。

首先考虑全为无向边的情况我们如何对其定向。

显然这个图一定为边双连通图,否则无论怎么定向都不行,而题目保证有解。

我们直接对这个图 dfs ,然后把树边都定为父亲到儿子的方向,返祖边都定到祖先的方向。

可以证明这样的图一定是强连通的。如果不是,那么必定存在一个点,

它在 dfs 树的后代中,没有返祖边连向这个点的祖先,那么原图就不是双连通图了,矛盾。

然后考虑混合图的情况。显然这个图一开始就是强连通的状态。

考虑一条无向边 $(u,v)$​

  1. 如果从 $u$ 到 $v$ 存在一条不经过这条边的路径,那么我们可以把这条无向边定向成 $v \to u$ 。
  2. 如果从 $v$ 到 $u$ 存在一条不经过这条边的路径,那么我们可以把这条无向边定向成 $u \to v$ 。

假设这两种情况都不满足,不妨设从 $u$ 不经过这条边能到达的节点集合 $S$ 。

由于原图强连通,则 $v$ 也可以不经过 $u$ 到达 $T = V \setminus S$​ 里的点

对于 $S$ 里的任何可以到达 $v$ 的节点,例如某个点 $s$ 到 $v$ 不用经过 $u$ ,

则 $u \leadsto s \leadsto v$ 就是一个不经过那条边的路径,这与假设矛盾。

所以 $S$ 里的任何点都可以不经过 $v$ 到 $u$ ,同理 $T$ 里的点到 $v$ 都不用经过 $u$ 。

考虑 $S$ 和 $T$ 之间的边,除了 $(u,v)$ 外一定还有一条边(双联通)。

不妨假设这条边是从 $S$ 中的 $p$ 到 $T$ 中的 $q$ ,则 $u \leadsto p \to q \leadsto v$ 就是一条不经过 $(u,v)$ 的路径,与假设矛盾。

因此,上述的两种情况必然会出现一种。

我们只需要一次考虑每条边,然后 dfs 出符合哪种情况并定向即可

时间复杂度 $\mathcal{O}(m^2)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(5e3 + 15))
#define addEdge(u, v) e[u].push_back(v)

int dfn, u[N], v[N], t[N], vis[N];
list<int> e[N]; list<int>::iterator i1[N], i2[N];
void dfs(int x)
{
    vis[x] = dfn;
    for(int y : e[x]) if(vis[y] != dfn) dfs(y);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int n, m; cin >> n >> m;
    for(int i = 1; i <= m; i++)
    {
        cin >> u[i] >> v[i] >> t[i];
        if(t[i]) addEdge(u[i], v[i]);
        else {
            addEdge(u[i], v[i]); --(i1[i] = e[u[i]].end());
            addEdge(v[i], u[i]); --(i2[i] = e[v[i]].end());
        }
    }
    for(int i = 1, j; i <= m; i++)
        if(t[i]) cout << "0\n";
        else {
            e[u[i]].erase(i1[i]); e[v[i]].erase(i2[i]);
            ++dfn; dfs(v[i]); j = (vis[u[i]] != dfn);
            cout << char('0' | j) << '\n';
            j ? addEdge(v[i], u[i]) : addEdge(u[i], v[i]);
        }
    return 0;
}

参考文献

[1] https://vfleaking.blog.uoj.ac/blog/694

[2] https://yhx-12243.github.io/OI-transit/records/uoj134.html

官方题解里,基图指的是将有向图所有的有向边替换为无向边后得到的无向图。


题外话

这道题也是2022年欠的题,导致现在写完这题有一种莫名的窒息感。


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录