「SDOI2017」天才黑客「优化建图最短路」
题意
给定一个n个点m条边的有向图和一个k个结点的字典树,每条边有一个时间花费c和口令,口令用字典树上一个结点表示。走一条边(u,v)的代价是这条边的时间花费加上你在u点口令和该边口令的lcp长度,并且到达v点后口令变成了该边口令。对于i = 2,3,...,n,你需要求出若你起初在1号结点且口令为空,到i的最小代价。
n, m\leq 5\times 10^4,k \leq 2\times 10^4。
题解
前后缀优化建图 + Dijkstra。
首先容易想到化边为点,赞不考虑原图的边权如何处理。
我们把一个点的出边和入边按口令在字典树上的dfs序排序,求出h_i表示第i条边和第i - 1条边的lcp,那么第l条边和第r条边的lcp就是\min_{i = l + 1}^r h_i(类似后缀数组思想)
由于这里lcp的式子是min,我们求的又是最短路,下面的方法便可以使用:假设当前在考虑u的入边出边。对于每条边i,排在i-1左边的入边和i右边的出边两两连边,同样对称地排在i-1左边的出边和i右边的入边两两连边,从入边向出边连,边权h_i。
这样就转化成了前后缀连边问题,我们可以构造以下图:
也就是说每条边在新图中对应4个点,对于每个u点,两排入边构成的点(按字典树dfs序排序,下同),两排出边构成的点,并且他们之间用0边连接。考虑h_i贡献时候,我们只需要把对应前后缀连接。比如排在i-1左边离i-1最近的入边是x,排在i右边离i最近的出边是y,入边1的x向出边1的y连边即可。入边2和出边2之间的连边同理。
当然还要把每条边的出边点向入边点连(一共4条边),边权就是原图该边边权,这样就解决了边权问题。最后超级源连向1的出边即可。
时间复杂度O(m \log m)。
#include <algorithm>
#include <cstdio>
#include <vector>
#include <queue>
#define pb push_back
using namespace std;
const int N = 2e5 + 50, M = N / 4, INF = 2e9 + 10;
struct FinalGraph {
struct Edge { int v, w; };
vector<Edge> G[N];
struct Node {
int u, d;
bool operator < (const Node &b) const { return d > b.d; }
};
int d[N], n;
void clr(int _n) {
n = _n;
for(int i = 1; i <= n; i ++) G[i].clear();
}
void link(int u, int v, int w) { G[u].pb((Edge) {v, w}); }
void Dijkstra() {
fill(d + 1, d + n, INF);
priority_queue<Node> pq; pq.push((Node) {n, d[n] = 0});
while(pq.size()) {
int u = pq.top().u, du = pq.top().d; pq.pop();
// printf("extended %d\n", u);
if(d[u] < du) continue ;
for(int i = 0; i < (int) G[u].size(); i ++) {
Edge &e = G[u][i];
if(d[e.v] > d[u] + e.w) {
pq.push((Node) {e.v, d[e.v] = d[u] + e.w});
}
}
}
}
} gp;
vector<int> T[M];
int n, m, k, lgk, d[M], f[M][20], dfn[M], idx;
int ID(int d, int u) { return (d - 1) * m + u; }
int lca(int u, int v) {
if(d[u] < d[v]) swap(u, v);
int c = d[u] - d[v];
for(int i = lgk - 1; ~ i; i --)
if(c >> i & 1) u = f[u][i];
if(u == v) return u;
for(int i = lgk - 1; ~ i; i --)
if(f[u][i] ^ f[v][i]) {
u = f[u][i]; v = f[v][i];
}
return f[u][0];
}
void dfs(int u) {
dfn[u] = ++ idx;
for(int i = 1; i < lgk; i ++)
f[u][i] = f[f[u][i - 1]][i - 1];
for(int i = 0; i < (int) T[u].size(); i ++) {
int v = T[u][i]; f[v][0] = u; d[v] = d[u] + 1; dfs(v);
}
}
struct Edge {
int id, uu;
bool operator < (const Edge &e) const { return dfn[uu] < dfn[e.uu]; }
};
vector<Edge> G[M], rG[M];
int main() {
int test; scanf("%d", &test);
while(test --) {
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= n; i ++) { G[i].clear(); rG[i].clear(); }
for(int i = 1; i <= k; i ++) T[i].clear();
for(lgk = 1; (1 << lgk) <= k; lgk ++) ;
idx = 0; gp.clr(ID(4, m) + 1);
for(int i = 1; i <= m; i ++) {
int u, v, w, uu;
scanf("%d%d%d%d", &u, &v, &w, &uu);
G[u].pb((Edge) {i, uu});
rG[v].pb((Edge) {i, uu});
gp.link(ID(3, i), ID(1, i), w);
gp.link(ID(4, i), ID(1, i), w);
gp.link(ID(3, i), ID(2, i), w);
gp.link(ID(4, i), ID(2, i), w);
}
for(int i = 0; i < (int) G[1].size(); i ++) {
gp.link(gp.n, ID(3, G[1][i].id), 0);
gp.link(gp.n, ID(4, G[1][i].id), 0);
}
for(int i = 1; i < k; i ++) {
int u, v;
scanf("%d%d%*d", &u, &v);
T[u].pb(v);
}
f[1][0] = d[1] = 0; dfs(1); vector<Edge> tmp;
for(int i = 1; i <= n; i ++) if(rG[i].size() && G[i].size()) {
sort(rG[i].begin(), rG[i].end());
sort(G[i].begin(), G[i].end());
tmp.clear();
for(int j = 0; j < (int) rG[i].size(); j ++) {
tmp.pb(rG[i][j]);
if(j) {
gp.link(ID(1, rG[i][j - 1].id), ID(1, rG[i][j].id), 0);
gp.link(ID(2, rG[i][j].id), ID(2, rG[i][j - 1].id), 0);
}
}
for(int j = 0; j < (int) G[i].size(); j ++) {
tmp.pb(G[i][j]);
if(j) {
gp.link(ID(3, G[i][j - 1].id), ID(3, G[i][j].id), 0);
gp.link(ID(4, G[i][j].id), ID(4, G[i][j - 1].id), 0);
}
}
sort(tmp.begin(), tmp.end());
for(int j = 1; j < (int) tmp.size(); j ++) {
int c = d[lca(tmp[j - 1].uu, tmp[j].uu)];
vector<Edge>::iterator it1 = upper_bound(rG[i].begin(), rG[i].end(), tmp[j - 1]);
vector<Edge>::iterator it2 = lower_bound(G[i].begin(), G[i].end(), tmp[j]);
if(it1 != rG[i].begin() && it2 != G[i].end()) {
it1 --;
gp.link(ID(1, it1 -> id), ID(3, it2 -> id), c);
}
it1 = upper_bound(G[i].begin(), G[i].end(), tmp[j - 1]);
it2 = lower_bound(rG[i].begin(), rG[i].end(), tmp[j]);
if(it1 != G[i].begin() && it2 != rG[i].end()) {
it1 --;
gp.link(ID(2, it2 -> id), ID(4, it1 -> id), c);
}
}
}
gp.Dijkstra();
for(int i = 2; i <= n; i ++) {
int ans = INF;
for(int j = 0; j < (int) rG[i].size(); j ++) {
ans = min(ans, gp.d[ID(1, rG[i][j].id)]);
ans = min(ans, gp.d[ID(2, rG[i][j].id)]);
}
printf("%d\n", ans);
}
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步