题意

一个 \(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. spring boot 基础 2018年5月3日

    主包下运行类@SpringBootApplication  此注解是核心注解,源码如下 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.R ...

  2. 【PHPStorm使用手册】php interpreter is not configured

    php interpreter is not configured 未配置php解析器 第一步: 引入下载好的php.exe 打开窗口 file -> settings -> Langua ...

  3. MySQL----mysql57服务突然不见了的,解决方法

    一. G:\MySQL\MySQL Server 5.7\bin>mysqld --initialize G:\MySQL\MySQL Server 5.7\bin>mysqld -ins ...

  4. springmvc复习笔记----springmvc最简单的第一个例子:RequestMapping试水

    结构 用到的包 web.xml <url-pattern>/</url-pattern>中可以换成其他的后缀*.do ,*. sb  …… <?xml version=& ...

  5. Not Found woff 字体库

    原因是,默认在IIS上是没有添加对*.woff,*.svg文件的Mime类型,因此在客户端请求此类文件时得到的都是404. 所以我们只需要在我们对应网站下的Mime类型中添加文件对应的类型就行了 .w ...

  6. Linux shell编写端口扫描脚本

    Linux shell编写端口扫描脚本 需求: 扫描特定主机 扫描特定主机的特定端口 扫描特定网段 扫描特定网段中哪些主机开放了特定的端口 源码如下: #/bin/bash #该脚本用于对特定目标主机 ...

  7. liunx搭建DHCP服务器以及DHCP中继服务器

    liunx搭建DHCP服务器以及DHCP中继服务器 一.实验拓扑 二.实验条件 虚拟机取消VMnet1和VMnet8的dhcp动态获取ip地址,以免影响实验 DHCPserver 网关以及DHCP中继 ...

  8. 【算法】LeetCode算法题-Palindrome Number

    这是悦乐书的第144次更新,第146篇原创 今天这道题和回文有关,即从前往后和从后往前是一样的,如"上海自来水来自海上"就是一个回文字符串,如整数121就是回文数,这些都是和回文相 ...

  9. March 11th, 2018 Week 11th Sunday

    All good things must come to an end. 好景无常. Love is when the other person's happiness is more importa ...

  10. 【Linux基础】Linux常用命令汇总

    3-1文件目录操作命令(cd pwd mkdir rmdir rm) 绝对路径:由根目录(/)开始写起的文件名或目录名称, 例如 /home/dmtsai/.bashrc: 相对路径:相对于目前路径的 ...