Description有一个供 K K K 个玩家玩的棋盘游戏。该游戏的棋盘由 N N N 个编号从 1 到 N N N 的单元格和 M M M 条编号从 1 到 M M M 的路径组成,其中路径 j j j (1 ≤ j ≤ M 1 ≤ j ≤ M 1 ≤ j ≤ M )双向连接着单元格 U j U_j U j 和 V j V_j V j 。
棋盘上有两种类型的单元格:重新激活单元格和停止单元格。
这些信息由长度为 N N N 的字符串 S S S 给出,S S S 由 0 0 0 和 1 1 1 组成,其中 S S S 的第 i i i 个字符(1 ≤ i ≤ N 1 ≤ i ≤ N 1 ≤ i ≤ N )是 0 表示单元格 i i i 是重新激活单元格,是 1 表示单元格 i i i 是停止单元格。
这个棋盘游戏由编号从 1 1 1 到 K K K 的 K K K 个玩家进行。每个玩家都有自己的棋子,游戏从每个玩家将其棋子放在特定的单元格上开始。一开始,玩家 p p p (1 ≤ p ≤ K 1 \leq p \leq K 1 ≤ p ≤ K )将其棋子放在单元格 X p X_p X p 上。注意,多个玩家的棋子可以放在同一个单元格上。
游戏随着每个玩家轮流进行而进行,从玩家 1 开始,按数字顺序进行。在玩家 p p p 完成其回合后,玩家 p + 1 p + 1 p + 1 开始回合(如果 p = K p = K p = K ,则玩家 1 开始回合)。每个玩家在其回合上执行以下操作:
选择与其棋子所在的单元格通过一条路径相连的一个单元格,并将其棋子移动到所选择的单元格上。
如果目标单元格是重新激活单元格,则重复步骤 1 并继续其回合。如果目标单元格是停止单元格,则结束其回合。
代表日本参加这个棋盘游戏的包括 JOI 君在内的由 K K K 名成员组成的团队,正在研究协作策略,以快速征服这个游戏。他们目前正在研究以下问题:
为了将玩家 1 的棋子放置在单元格 T T T 上,K K K 名玩家需要的最小总移动次数是多少?即使在回合中途,如果玩家 1 的棋子被放置在单元格 T T T 上,也认为满足条件。
给定关于游戏棋盘和每个玩家棋子的初始放置位置的信息,编写一个程序来计算每个 T = 1 , 2 , … , N T = 1, 2, \ldots, N T = 1 , 2 , … , N 对应的问题的答案。
N , M , K ≤ 5 × 1 0 4 N,M,K\leq 5\times 10^4 N , M , K ≤ 5 × 1 0 4 。
Solution显然 2 ∼ k 2\sim k 2 ∼ k 人对答案的贡献只与进行的轮数 x x x 有关。
设 f ( i , x ) f(i,x) f ( i , x ) 表示第 i i i 个人走 x x x 轮的最小步数,F ( x ) = ∑ i = 2 k f ( i , x ) F(x)=\sum_{i=2}^{k}{f(i,x)} F ( x ) = ∑ i = 2 k f ( i , x ) 。那么 f ( i , x ) f(i,x) f ( i , x ) 只有两种可能的贡献:
i i i 先走到一个最近的停止点然后一直跳出去再跳回来。
i i i 走到一个最近的邻域有停止点的停止点然后每次反复横跳。
不妨设 d i s 1 , i dis_{1,i} d i s 1 , i 表示 i i i 走到最近的停止点的距离。第一种贡献很容易得到是 d i s 1 , i + 2 ( x − 1 ) dis_{1,i}+2(x-1) d i s 1 , i + 2 ( x − 1 ) 。
对于第二种情况,设 i i i 走到一个邻域有停止点的停止点轮数为 c c c ,步数为 d d d 。那么如果 c ≤ x c\leq x c ≤ x ,可以得到贡献是 d + x − c d+x-c d + x − c 。同时由于没多走一轮,步数至少增加一,所以 d + x − c d+x-c d + x − c 对于 c > x c>x c > x 的情况也成立。
于是对第二种情况的 d − c d-c d − c 跑 01bfs 即可求出所有 f ( i , x ) f(i,x) f ( i , x ) 的值。
设 G ( x , u ) G(x,u) G ( x , u ) 表示 1 1 1 走恰好 x x x 轮到 u u u 的最小步数,则 a n s u = min x = 1 n ( F ( x ) + G ( x , u ) ) ans_u=\min_{x=1}^{n}{\left(F(x)+G(x,u)\right)} a n s u = min x = 1 n ( F ( x ) + G ( x , u ) ) 。
由于 f ( i , x ) f(i,x) f ( i , x ) 形如 min ( d 1 + x , d 2 + 2 x ) \min(d_1+x,d_2+2x) min ( d 1 + x , d 2 + 2 x ) ,所以 F ( x ) F(x) F ( x ) 一定是一个凸函数,且只有最多 k k k 段。
那么对于 k k k 比较小的情况可以找到函数的每段 y = p x + q y=px+q y = p x + q ,将 p p p 放到最短路的边权上跑 dijkstra 即可做到 O ( n k log n ) O(nk\log n) O ( n k log n ) 。
又因为 x x x 每增大 1 1 1 ,F ( x ) F(x) F ( x ) 至少减 k − 1 k-1 k − 1 ,要想让答案更优,G ( x , u ) G(x,u) G ( x , u ) 也要减少至少 k − 1 k-1 k − 1 ,而 G ( x , u ) ≤ n G(x,u)\leq n G ( x , u ) ≤ n ,所以只会减少至多 n k − 1 \frac{n}{k-1} k − 1 n 轮。
设 d i s 3 , i dis_{3,i} d i s 3 , i 表示 i i i 走到一个停止点的最小轮数,则能对 i i i 的答案产生影响的轮数一定在 [ d i s 3 , i , d i s 3 , i + n k − 1 ] \left[dis_{3,i},dis_{3,i}+\frac{n}{k-1}\right] [ d i s 3 , i , d i s 3 , i + k − 1 n ] 内,对这个进行分层图 bfs 即可做到 O ( n 2 k ) O\left(\frac{n^2}{k}\right) O ( k n 2 ) 。
将阈值设为 n log n \sqrt{\frac{n}{\log n}} l o g n n 时可以得到时间复杂度为 O ( n n log n ) O\left(n\sqrt{n\log n}\right) O ( n n log n ) 。
Code1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 #include <bits/stdc++.h> #define int int64_t const int kMaxN = 5e4 + 5 , kLim = 100 , kInf = 1e12 ;int n, m, k;int a[kMaxN], dis0[kMaxN], dis1[kMaxN], dis2[kMaxN], ans[kMaxN], f[kMaxN];bool op[kMaxN]; std::vector<int > G[kMaxN];void bfs0 () { std::queue<int > q; for (int i = 1 ; i <= n; ++i) dis0[i] = kInf; dis0[a[1 ]] = 0 , q.emplace (a[1 ]); for (; !q.empty ();) { int u = q.front (); q.pop (); if (op[u] && u != a[1 ]) continue ; for (auto v : G[u]) { if (dis0[v] == kInf) { dis0[v] = dis0[u] + 1 , q.emplace (v); } } } }void bfs1 () { std::queue<int > q; for (int i = 1 ; i <= n; ++i) { dis1[i] = kInf; bool fl = 0 ; for (auto j : G[i]) fl |= op[j]; if (fl) dis1[i] = 1 , q.emplace (i); } for (; !q.empty ();) { int u = q.front (); q.pop (); for (auto v : G[u]) { if (dis1[v] == kInf) { dis1[v] = dis1[u] + 1 , q.emplace (v); } } } }void bfs2 () { std::deque<int > q; for (int i = 1 ; i <= n; ++i) { dis2[i] = kInf; bool fl = 0 ; for (auto j : G[i]) fl |= op[j]; if (op[i] && fl) dis2[i] = 0 , q.emplace_back (i); } for (; !q.empty ();) { int u = q.front (); q.pop_front (); for (auto v : G[u]) { int dv = dis2[u] + 1 - op[u]; if (dv < dis2[v]) { dis2[v] = dv; if (op[u]) q.emplace_front (v); else q.emplace_back (v); } } } }void getf () { static int g[kMaxN] = {0 }; for (int c = 2 ; c <= k; ++c) { int x = a[c]; int p = std::min (std::max <int >(dis2[x] - dis1[x] + 3 , 1 ), n + 1 ); f[1 ] += dis1[x] - 2 , f[p] -= dis1[x] - 2 , f[p] += dis2[x]; g[1 ] += 2 , g[p] -= 2 , g[p] += 1 ; } for (int i = 1 ; i <= n; ++i) f[i] += f[i - 1 ], g[i] += g[i - 1 ]; for (int i = 1 ; i <= n; ++i) f[i] += i * g[i]; }namespace Sub1 {void getans (int k, int b) { static int dis[kMaxN]; static bool vis[kMaxN] = {0 }, vv[kMaxN] = {0 }; std::priority_queue<std::pair<int , int >> q; for (int i = 1 ; i <= n; ++i) dis[i] = kInf, vis[i] = vv[i] = 0 ; dis[a[1 ]] = 0 , q.emplace (0 , a[1 ]); for (; !q.empty ();) { int u = q.top ().second; q.pop (); if (vis[u]) continue ; vis[u] = 1 ; for (auto v : G[u]) { int w = 1 + k * (op[u] && u != a[1 ]); if (dis[v] > dis[u] + w) { dis[v] = dis[u] + w, q.emplace (-dis[v], v); vv[v] = (vv[u] | (w > 1 )); } } } for (int i = 1 ; i <= n; ++i) if (vv[i]) ans[i] = std::min (ans[i], dis[i] + b); }void solve () { int nowk = -1 ; for (int i = 2 ; i <= n; ++i) { int k = f[i] - f[i - 1 ]; if (k != nowk) { getans (k, f[i] - i * k); nowk = k; } } } } namespace Sub2 {int lim, dis3[kMaxN], dis4[kMaxN][kMaxN / (kLim - 1 ) + 5 ];void bfs3 () { std::deque<int > q; for (int i = 1 ; i <= n; ++i) dis3[i] = kInf; q.emplace_back (a[1 ]), dis3[a[1 ]] = 0 ; for (; !q.empty ();) { int u = q.front (); q.pop_front (); int w = (op[u] && u != a[1 ]); for (auto v : G[u]) { if (dis3[v] > dis3[u] + w) { dis3[v] = dis3[u] + w; if (!w) q.emplace_front (v); else q.emplace_back (v); } } } }void bfs4 () { std::queue<std::pair<int , int >> q; for (int i = 1 ; i <= n; ++i) for (int j = 0 ; j <= lim; ++j) dis4[i][j] = kInf; dis4[a[1 ]][0 ] = 0 , q.emplace (a[1 ], 0 ); for (; !q.empty ();) { auto [u, t] = q.front (); q.pop (); int w = (op[u] && u != a[1 ]); for (auto v : G[u]) { int tv = t + dis3[u] + w - dis3[v]; if (tv <= lim && dis4[v][tv] > dis4[u][t] + 1 ) { dis4[v][tv] = dis4[u][t] + 1 ; q.emplace (v, tv); } } } }void solve () { lim = n / (k - 1 ); bfs3 (), bfs4 (); for (int i = 1 ; i <= n; ++i) for (int j = 0 ; j <= lim; ++j) ans[i] = std::min (ans[i], dis4[i][j] + f[dis3[i] + j]); } } void dickdreamer () { std::cin >> n >> m >> k; for (int i = 1 ; i <= m; ++i) { int u, v; std::cin >> u >> v; G[u].emplace_back (v), G[v].emplace_back (u); } std::string str; std::cin >> str; for (int i = 1 ; i <= n; ++i) op[i] = str[i - 1 ] - '0' ; for (int i = 1 ; i <= k; ++i) std::cin >> a[i]; bfs0 (), bfs1 (), bfs2 (), getf (); for (int i = 1 ; i <= n; ++i) ans[i] = dis0[i]; if (!std::count (op + 1 , op + 1 + n, 1 )) { for (int i = 1 ; i <= n; ++i) std::cout << ans[i] << '\n' ; return ; } if (k <= kLim) Sub1::solve (); else Sub2::solve (); ans[a[1 ]] = 0 ; for (int i = 1 ; i <= n; ++i) std::cout << ans[i] << '\n' ; }int32_t main () {#ifdef ORZXKR freopen ("in.txt" , "r" , stdin); freopen ("out.txt" , "w" , stdout);#endif std::ios::sync_with_stdio (0 ), std::cin.tie (0 ), std::cout.tie (0 ); int T = 1 ; while (T--) dickdreamer (); return 0 ; }