「找性质+比较强的分层图」 [Codeforces 1209F] Koala and Notebook

皎月半洒花

2020-06-16 21:28:41

Solution

这题是真的有趣。虽然场上并没有去写(感觉写了也是不可能过的,因为细节比较多)。 一开始觉得「难道不是把出边 xjb 排个序就做完了嘛」。然后发现被叉了,心情郁闷(x 考虑如何比较「编号相连得到的十进制数」的大小,那必然是先比较位数,再从头开始比较权值。于是就有个特别强的建图技巧:拆边。考虑把一条编号为 $(\overline{a_1a_2\cdots a_k})_{10}$ 的边拆成两条有向链,链上的边的边权分别为 $a_1,a_2\cdots a_k$。 然后考虑进行类似分层图的 BFS。这个分层图的细节很丰富: 0、分层的目的是,控制遍历顺序,使得 BFS 的最优性也在当前问题中体现。即使得对于每个点,最先被遍历时一定是最短路。 1、首先要先忽略边权进行分层。 2、其次考虑如果存在多条出边,必然是走权最小的那条边。 3、只符合上面两条还是不对的。因为考虑如果某个点 $t$ 有两条入边 $e_1,e_2$ 满足 $val(e_1)<val(e_2)$,他们在 BFS 树上的深度相同,那么如果单纯按点来转移,可能会出现先走 $e_2$ 再走 $e_1$ 的情况,而此时 $t$ 已经被更新完毕就会导致出错。所以还要按照边权从 $0$ 到 $9$ 进行再分层。 可以知道这样做最后复杂度一定不会超过 $O(10\times m)$ 。 ```[cpp int ob ; int cnt ; int n, m ; int arr[M] ; int f[N] ; int g[N] ; int T ; vector <int> q[N] ; vector <int> E[N][10] ; int main(){ cin >> n >> m ; int x, y, z ; cnt = n ; for (int i = 1 ; i <= m ; ++ i){ x = qr(), y = qr() ; ob = 0 ; z = i ; while (z) arr[++ ob] = z % 10, z /= 10 ; z = x ; for (int j = ob ; j > 1 ; -- j) E[x][arr[j]].push_back(++ cnt), x = cnt ; E[x][arr[1]].push_back(y) ; for (int j = ob ; j > 1 ; -- j) E[y][arr[j]].push_back(++ cnt), y = cnt ; E[y][arr[1]].push_back(z) ; } g[1] = T = 1 ; q[1].push_back(1) ; for (int d = 1 ; d <= T ; ++ d){ // if (!q[d].size()) break ; for (z = 0 ; z < 10 ; ++ z){ auto t = q[d].begin() ; while (t != q[d].end()){ x = *t ; for (auto y : E[x][z]){ if (g[y]) continue ; f[y] = (f[x] * 10ll + z) % P ; g[y] = 1, q[T + 1].push_back(y) ; } ++ t ; } if (q[T + 1].size()) ++ T ; } } for (int i = 2 ; i <= n ; ++ i) printf("%d\n", f[i]) ; return 0 ; } ``` #