YC314A [ 20240704 CQYC省选模拟赛 T1 ] 士兵(solider)
题意
给定一张 \(n\) 个点 \(m\) 条边的有向图,每条边上有一个字母。
\(q\) 次询问,每次询问 \(s \to t\) 中的最短回文路径的长度是多少。
\(n \le 10 ^ 3, m \le 10 ^ 5\)
Sol
区间 \(\text{dp}\),设 \(f_{i, j}\) 表示从 \(i\) 到 \(j\) 的最短回文路径的长度。
每次枚举一条边 \(l \to i\),以及 \(j \to r\),判断两条边的字母是否相等,暴力转移即可。
直接对于每个点跑一遍 \(\text{bfs}\),复杂度为 \(O(m ^ 2)\)。
考虑优化,将一次过程拆成两次。
设 \(g_{i, j, c}\) 表示从 \(i\) 到 \(j\) 的最短回文路径,且最后一条边为的字母为 \(c\)。
对于一个状态 \(f_{x, y}\),枚举一条出边 \(y \to z\),转移到 \(g_{x, z, c}\) 上。
这样就变成了有两种边的 \(\text{bfs}\),考虑用两个队列来存,这样每次取两个队列中 \(dis\) 较小的即可。
复杂度:\(O(nm + 26n ^ 2)\)
Code
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <array>
#include <tuple>
#include <queue>
#include <vector>
#include <assert.h>
#define pii pair <int, int>
#define tupl tuple <int, int, char>
using namespace std;
#ifdef ONLINE_JUDGE
/* #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++) */
/* char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf; */
#endif
int read() {
int p = 0, flg = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') flg = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
p = p * 10 + c - '0';
c = getchar();
}
return p * flg;
}
void write(int x) {
if (x < 0) {
x = -x;
putchar('-');
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
bool _stmer;
#define fi first
#define se second
const int N = 1e3 + 5, M = 1e5 + 5, inf = 1e9 + 7;
char strbuf[2];
array <array <vector <int>, 31>, N> G, T;
queue <pii> q1;
queue <tupl> q2;
array <array <int, N>, N> f;
array <array <pii, N>, N> pref;
array <array <array <int, 31>, N>, N> g;
array <array <array <pii, 31>, N>, N> preg;
void bfs(int n, int m) {
while (!q1.empty() || !q2.empty()) {
bool flg1 = q1.empty(), flg2 = q2.empty();
int u1 = 0, u2 = 0, v1 = 0, v2 = 0, l1 = 0, l2 = 0, c = 0;
if (!flg1) u1 = q1.front().fi, v1 = q1.front().se, l1 = f[u1][v1];
if (!flg2) tie(u2, v2, c) = q2.front(), l2 = g[u1][v1][c];
if (!flg1 && (flg2 || l1 <= l2)) {
q1.pop();
c = 0;
for ( ; c < 26; c++) {
for (auto k : G[v1][c]) {
// if (v1 == 3 && c == 'm' - 'a')
// cerr << u1 << endl;
if (~g[u1][k][c]) continue;
g[u1][k][c] = f[u1][v1] + 1;
preg[u1][k][c] = make_pair(v1, c);
q2.push(make_tuple(u1, k, c));
}
}
}
else {
q2.pop();
for (auto k : T[u2][c]) {
if (~f[k][v2]) continue;
f[k][v2] = g[u2][v2][c] + 1;
pref[k][v2] = make_pair(u2, c);
q1.push(make_pair(k, v2));
}
}
}
}
array <int, M * 20> ans;
void print(int x, int y, int k, int l, int r) {
if (x == y) return;
if (k == -1) {
ans[l] = pref[x][y].se;
print(pref[x][y].fi, y, ans[l], l + 1, r);
}
else {
ans[r] = preg[x][y][k].se;
print(x, preg[x][y][k].fi, -1, l, r - 1);
}
}
bool _edmer;
int main() {
cerr << (&_stmer - &_edmer) / 1024.0 / 1024.0 << "MB\n";
int n = read(), m = read(), tp = read();
/* int n = read(), m = read(); */
for (int i = 0; i <= n; i++) f[i].fill(-1);
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++) g[i][j].fill(-1);
for (int i = 1; i <= n; i++)
f[i][i] = 0, q1.push(make_pair(i, i));
for (int i = 1; i <= m; i++) {
int x = read(), y = read();
scanf("%s", strbuf);
G[x][strbuf[0] - 'a'].push_back(y), T[y][strbuf[0] - 'a'].push_back(x);
q1.push(make_pair(x, y)), f[x][y] = 1, pref[x][y] = make_pair(x, strbuf[0] - 'a');
}
bfs(n, m);
// cerr << f[3][5] << endl;
int q = read();
while (q--) {
int x = read(), y = read();
write(f[x][y]), putchar(32);
// assert(f[x][y] <= m);
if (tp == 1 && ~f[x][y]) {
print(x, y, -1, 1, f[x][y]);
for (int i = 1; i <= f[x][y]; i++)
putchar(ans[i] + 'a');
}
puts("");
}
/*
int q = read(), x = read(); q--;
while (q--) {
int y = read();
write(f[x][y]), putchar(32);
assert(f[x][y] <= m);
if (~f[x][y]) {
print(x, y, -1, 1, f[x][y]);
for (int i = 1; i <= f[x][y]; i++)
putchar(ans[i] + 'a');
}
puts("");
x = y;
}
*/
return 0;
}