题意

一个 \(n\) 点 \(m\) 边的有向图,还有一棵 \(k\) 个节点的 trie ,每条边上有一个字符串,可以用 trie 的根到某个节点的路径来表示。每经过一条边,当前携带的字符串就会变成边上的字符串,经过一条边的代价是边权+边上的字符串和当前字符串的 lcp,问从 1 号点走到所有点的最小代价。

\(n,m\le 50000, k\le 20000\)

分析

  • 将边看成点,如果有 \(e1 \rightarrow x\rightarrow e2\) , 连边 \(e1 \rightarrow e2\) ,代价就是 lcp ,考虑优化建图。
  • 实际本题的字典树是一个提示,可以将一个点的子节点按照字符大小遍历,根据后缀数组求 \(height\) 的性质容易知道两个点 \(u,v\) 的 lca 就是他们 dfs 序中间的所有相邻点的 lca 的深度最小的那一个
  • 这个结论也可以通过点作为 lca 的依据(至少两个子树内有关键点)得到,也就是说一定可以通过这种方式表示出这两个点的lca。所以前缀后缀优化建图即可。
  • 复杂度 \(O(nlogn)\) 。
  • 注意可能出现一条出边一条入边的字符串相同的情况,所以每个前缀节点还要直接连向对应的后缀节点。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define go(u) for(int i = head[u], v = e[i].to; i; i=e[i].lst, v=e[i].to)
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define pb push_back
#define re(x) memset(x, 0, sizeof x)
inline int gi() {
int x = 0,f = 1;
char ch = getchar();
while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) { x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
return x * f;
}
template <typename T> inline bool Max(T &a, T b){return a < b ? a = b, 1 : 0;}
template <typename T> inline bool Min(T &a, T b){return a > b ? a = b, 1 : 0;}
const int N = 5e5 + 7, inf = 0x7fffffff;
int n, m, ndc, edc, T, k;
int a[N], b[N], c[N], d[N];
vector<int> A[N], B[N];
struct edge {
int lst, to, c;
edge(){}edge(int lst, int to, int c):lst(lst), to(to), c(c){}
};
namespace Trie {
edge e[N];
int edc, tim, pc;
int head[N], in[N], pos[N], mi[N][20], dep[N], Log[N];
void Add(int a, int b) {
e[++edc] = edge(head[a], b, 0), head[a] = edc;
}
void dfs(int u) {
mi[pos[u] = ++pc][0] = u;
in[u] = ++tim;
go(u) {
dep[v] = dep[u] + 1;
dfs(v);
mi[++pc][0] = u;
}
}
void rmq_init() {
Log[1] = 0;
for(int i = 2; i <= pc; ++i) Log[i] = Log[i >> 1] + 1;
for(int k = 1; 1 << k <= pc; ++k)
for(int i = 1; i + (1 << k) - 1 <= pc; ++i)
mi[i][k] = in[mi[i][k - 1]] < in[mi[i + (1 << k - 1)][k - 1]] ? mi[i][k - 1] : mi[i + (1 << k - 1)][k - 1];
}
int dLca(int l, int r) {
if(l == r) return dep[l];
l = pos[l], r = pos[r];
if(l > r) swap(l, r);
int k = Log[r - l + 1];
return in[mi[l][k]] < in[mi[r - (1 << k) + 1][k]] ? dep[mi[l][k]] : dep[mi[r - (1 << k) + 1][k]];
}
}
bool cmp(int a, int b) {
return Trie::in[a] < Trie::in[b];
}
int vt[N], head[N], st, ed, vc;
int pre1[N], pre2[N], suf1[N], suf2[N];
edge e[N * 20];
struct Heap {
int u, dis;
Heap(){}Heap(int u, int dis):u(u), dis(dis){}
bool operator <(const Heap &rhs) const {
return rhs.dis < dis;
}
};
priority_queue<Heap>Q;
int dis[N], vis[N], ans[N], from[N];
void dijk() {
fill(dis, dis + ndc + 1, inf);
fill(vis, vis + ndc + 1, 0);
dis[st] = 0;
Q.push(Heap(st, 0));
while(!Q.empty()) {
int u = Q.top().u; Q.pop();
if(vis[u]) continue;vis[u] = 1;
go(u)if(Min(dis[v], dis[u] + e[i].c + c[v])) {
Q.push(Heap(v, dis[v]));
from[v] = u;
}
}
fill(ans, ans + ndc + 1, inf);
for(int i = 1; i <= m; ++i) Min(ans[b[i]], dis[i]);
rep(i, 2, n) printf("%d\n", ans[i]);
}
void Add(int a, int b, int c) {
e[++edc] = edge(head[a], b, c), head[a] = edc;
}
void prepare(int u) {
vc = 0;
for(auto v:A[u]) vt[++vc] = d[v];
for(auto v:B[u]) vt[++vc] = d[v];
sort(vt + 1, vt + 1 + vc, cmp);
vc = unique(vt + 1, vt + 1 + vc) - vt - 1;
rep(i, 1, vc) pre1[i] = ++ndc;
rep(i, 1, vc) pre2[i] = ++ndc, Add(pre1[i], pre2[i], Trie::dep[vt[i]]);
rep(i, 1, vc) suf1[i] = ++ndc;
rep(i, 1, vc) suf2[i] = ++ndc, Add(suf1[i], suf2[i], Trie::dep[vt[i]]);
for(auto v:A[u]) {
int p = lower_bound(vt + 1, vt + 1 + vc, d[v], cmp) - vt;
Add(v, pre1[p], 0);
Add(v, suf1[p], 0);
}
for(auto v:B[u]) {
int p = lower_bound(vt + 1, vt + 1 + vc, d[v], cmp) - vt;
Add(pre2[p], v, 0);
Add(suf2[p], v, 0);
}
rep(i, 1, vc - 1) {
Add(pre1[i], pre1[i + 1], 0);
Add(pre2[i], pre2[i + 1], 0);
Add(pre1[i], pre2[i + 1], Trie::dLca(vt[i], vt[i + 1]));
}
for(int i = vc; i >= 2; --i) {
Add(suf1[i], suf1[i - 1], 0);
Add(suf2[i], suf2[i - 1], 0);
Add(suf1[i], suf2[i - 1], Trie::dLca(vt[i], vt[i - 1]));
}
}
int main() {
T = gi();
while(T--) {
n = gi(), m = gi(), k = gi();
ndc = m;
Trie::edc = edc = 0;
Trie::tim = Trie::pc = 0;
fill(Trie::head, Trie::head + k + 1, 0);
re(head);
rep(i, 1, n) A[i].clear(), B[i].clear(); rep(i, 1, m) {
a[i] = gi(), b[i] = gi(), c[i] = gi(), d[i] = gi();
A[b[i]].pb(i);
B[a[i]].pb(i);
}
rep(i, 1, k - 1) {
int u = gi(), v = gi(), w = gi();
Trie::Add(u, v);
}
Trie::dfs(1);
Trie::rmq_init();
rep(i, 1, n) prepare(i);
st = ++ndc;
for(auto v:B[1]) Add(st, v, 0);
dijk();
fill(c, c + ndc + 1, 0);
}
return 0;
}

[SDOI2017]天才黑客[最短路、前缀优化建图]的更多相关文章

  1. 洛谷P3783 [SDOI2017]天才黑客(前后缀优化建图+虚树+最短路)

    题面 传送门 题解 去看\(shadowice\)巨巨写得前后缀优化建图吧 话说我似乎连线段树优化建图的做法都不会 //minamoto #include<bits/stdc++.h> # ...

  2. 洛谷 P3783 - [SDOI2017]天才黑客(前后缀优化建图)

    题面传送门 神仙题一道. 首先注意到这里的贡献涉及到边的顺序,并且只与相邻的边是什么有关,因此不难想到一个做法--边转点,点转边,具体来说对于每条边 \(e\),我们将其拆成两个点 \(in_e,ou ...

  3. 【SDOI2017】天才黑客(前后缀优化建图 & 最短路)

    Description 给定一张有向图,\(n\) 个点,\(m\) 条边.第 \(i\) 条边上有一个边权 \(c_i\),以及一个字符串 \(s_i\). 其中字符串 \(s_1, s_2, \c ...

  4. CF1007D. Ants(树链剖分+线段树+2-SAT及前缀优化建图)

    题目链接 https://codeforces.com/problemset/problem/1007/D 题解 其实这道题本身还是挺简单的,这里只是记录一下 2-SAT 的前缀优化建图的相关内容. ...

  5. 【CF587D】Duff in Mafia 二分+前缀优化建图+2-SAT

    [CF587D]Duff in Mafia 题意:给你一张n个点m条边的无向图,边有颜色和边权.你要从中删去一些边,满足: 1.任意两条删掉的边没有公共的顶点.2.任意两条剩余的.颜色相同的边没有公共 ...

  6. BZOJ.3495.[PA2010]Riddle(2-SAT 前缀优化建图)

    题目链接 每个城市要么建首都要么不建,考虑2-SAT 这样一个国家内城市两两连边是很显然的,但是边数为O(n^2) 每个国家中仅有一个建首都,考虑新建前缀S[i]=1/0这2n个点表示当前国家的[1, ...

  7. 洛谷3783 SDOI2017 天才黑客(最短路+虚树+边转点+线段树优化建图)

    成功又一次自闭了 怕不是猪国杀之后最自闭的一次 一看到最短路径. 我们就能推测这应该是个最短路题 现在考虑怎么建图 根据题目的意思,我们可以发现,在本题中,边与边之间存在一些转换关系,但是点与点之间并 ...

  8. BZOJ4912 [Sdoi2017]天才黑客 【虚树 + 最短路】

    题目链接 BZOJ4912 题解 转移的代价是存在于边和边之间的 所以把边看做点,跑最短路 但是这样做需要把同一个点的所有入边和所有出边之间连边 \(O(m^2)\)的连边无法接受 需要优化建图 膜一 ...

  9. [LOJ#2270][BZOJ4912][SDOI2017]天才黑客

    [LOJ#2270][BZOJ4912][SDOI2017]天才黑客 试题描述 SD0062 号选手小 Q 同学为了偷到 SDOI7012 的试题,利用高超的黑客技术潜入了 SDOI 出题组的内联网的 ...

随机推荐

  1. Testlink1.9.17使用方法(第六章 测试计划制定)

    第六章 测试计划制定 QQ交流群:585499566 在TestLink系统中,一个完整的测试计划包括:集成测试阶段.系统测试阶段. 一. 创建测试计划 1,点击主页上[测试计划管理] 2,在“测试计 ...

  2. springboot 学习之路 6(定时任务)

    目录:[持续更新.....] spring 部分常用注解 spring boot 学习之路1(简单入门) spring boot 学习之路2(注解介绍) spring boot 学习之路3( 集成my ...

  3. LNMP + Apache 架构配置

    从事前端开发已3年有余,越来越发现前端开发要学习的知识已不仅仅是html+css+js那么简单了,2017年市场上就有了大前端的概念,可以说对前端工程师的要求也越来越高了,从招聘的要求中可以看到熟悉一 ...

  4. spring静态代理和动态代理

    本节要点: Java静态代理 Jdk动态代理 1 面向对象设计思想遇到的问题 在传统OOP编程里以对象为核心,并通过对象之间的协作来形成一个完整的软件功能,由于对象可以继承,因此我们可以把具有相同功能 ...

  5. replace函数使用方法

    Replace函数的含义~ 用新字符串替换旧字符串,而且替换的位置和数量都是指定的. replace函数的语法格式 =Replace(old_text,start_num,num_chars,new_ ...

  6. c/c++ 图相关的函数(二维数组法)

    c/c++ 图相关的函数(二维数组法) 遍历图 插入顶点 添加顶点间的线 删除顶点 删除顶点间的线 摧毁图 取得与v顶点有连线的第一个顶点 取得与v1顶点,v1顶点之后的v2顶点的之后的有连线的第一个 ...

  7. lua时间戳和日期转换及踩坑

    介绍lua的日期函数常用方法及我的一个踩坑. 时间戳转日期 os.date("%Y%m%d%H",unixtime) --os.date("%Y%m%d%H", ...

  8. JavaSE: Java 5 新特性

    Java5新特性 1.Java 语言 1.1 Generics 1.2 foreach 1.3 自动拆箱装箱 1.4 enum 1.5 可变参数 varargs 1.6 static import 1 ...

  9. 2016某知名互联网公司PHP面试题及答案(续)

    1 写出mysql中,插入数据,读出数据,更新数据的语句 INSERT INTO 表名 VALUES ("",""): SELECT * FROM 表名:. U ...

  10. Linux中的文件查找技巧

    前言 Linux常用命令中,有些命令可以帮助我们查找二进制文件,帮助手册或源文件的位置,也有的命令可以帮助我们查找磁盘上的任意文件,今天我们就来看看这些命令如何使用. witch witch命令会在P ...