[LOJ#2270][BZOJ4912][SDOI2017]天才黑客html
試題描述node
SD0062 號選手小 Q 同窗爲了偷到 SDOI7012 的試題,利用高超的黑客技術潛入了 SDOI 出題組的內聯網的中央控制系統,然而這個內聯網除了配備有中央控制系統,還爲內聯網中的每條單向網線設定了特殊的通訊口令,這裏通訊口令是一個字符串,不一樣網線的口令可能不一樣。這讓小 Q 同窗感受有些棘手, 不過這根本難不倒他,很快他就分析出了整個內聯網的結構。ios
內聯網中有 n 個節點(從 1 到 n 標號)和 m 條單向網線,中央控制系統在第 1 個節點上,每條網線單向鏈接內聯網中的某兩個節點,從 1 號節點出發通過若干條網線總能到達其餘任意一個節點。每一個節點均可以運行任意的應用程序,應用程序會攜帶一條通訊口令,當且僅當程序的口令與網線的口令相同時,程序才能經過這條網線到達另外一端的節點繼續運行,而且經過每條網線都須要花費必定的時間。git
每一個應用程序能夠在任意一個節點修改通訊口令,修改通訊口令花費的時間能夠忽略不計,可是爲了減少修改量,須要先調用一個子程序來計算當前程序的口令和網線的口令的最長公共前綴(記其長度爲 len),因爲獲取網線的口令的某個字符會比較耗時,調用一次這個子程序須要花費 len 個單位時間。算法
除此以外,小 Q 同窗還在中央控制系統中發現了一個字典,每條網線的口令都是字典中的某個字符串。具體來講,這個字典是一棵 k 個節點(從 1 到 k 標號)的有根樹,其中根是第 1 個節點,每條邊上有一個字符,字符串 S 在字典中當且僅當存在某個點 u 使得從根節點出發往下走到 u 的這條路徑上的字符順次拼接構成 S。測試
如今小 Q 同窗在 1 號節點同時開啓了 n−1 個應用程序,這些應用程序同時運行且互不干擾,每一個程序的通訊口令都爲空,他但願用最短的時間把這些程序分別發送到其餘節點上,你須要幫小 Q 同窗分別計算出發送到第 i(=2,3,…,n) 個節點的程序完成任務的最短期。優化
輸入ui
第一行是一個正整數 T,表示測試數據的組數,spa
對於每組測試數據,code
第一行是三個整數 n,m,k,分別表示內聯網的節點數、內聯網的網線條數、字典樹的節點數,
接下來 m 行,每行包含四個整數 ai,bi,ci, di(1≤ai,bi≤n,0≤ci≤20000,1≤di≤k),表示沿着這條網線能夠從第 ai 個節點花費 ci 個單位時間到達第 bi 個節點,網線的口令是由從字典樹的根到 di 這個點的路徑上的字符順次拼接構成的字符串(可能爲空),須要注意的是這個內聯網可能有自環和重邊,
接下來 k−1 行,每行包含三個整數 ui,vi,wi(1≤ui,vi≤k,1≤wi≤20000),表示字典樹上有一條 ui→vi 的邊,邊上有字符 wi,保證給出的邊構成一棵以 1 爲根的有根樹,而且每一個點連出去的邊上的字符互不相同。
輸出
輸入示例
1 4 4 6 1 2 2 5 2 3 2 5 2 4 1 6 4 2 1 6 1 2 1 2 3 1 3 4 1 4 5 2 1 6 2
輸出示例
2 7 3
數據規模及約定
對於 100% 的數據,T≤10,2≤n≤50000,1≤m≤50000,1≤k≤20000,保證知足 n>5000 或 m>5000 的數據不超過 2 組。
題解
這題思路的前半部分挺妙的,後半部分就喪心病狂了。。。
首先考慮暴力,咱們能夠把原圖上的每一個邊做爲新圖的節點,這樣咱們最短路轉移的時候就能夠方便地知道上一次和這一次在 Trie 樹上的 lca。可是暴力連的話顯然有 n2 條邊,因而考慮優化建圖。
先區分好三個名詞:原圖指輸入給出的有向圖,Trie 樹指的是輸入給出的有根樹,新圖指的是算法新建的圖。
對於原圖中每一個節點,咱們把和這個節點相連的邊在 Trie 樹上的節點拿出來創建一棵虛樹。
那麼咱們就能夠輕易的知道以某個節點爲 lca 的點對有哪些了,那麼在新圖中建立兩個用虛樹 DFS 序表示的線段樹,兩個線段樹分別管原圖中的入邊和出邊(把它們分別稱爲入線段樹和出線段樹)。入邊在新圖中對應的節點向入線段樹的葉節點連邊,入線段樹的節點之間是自底向上連邊;出線段樹葉節點向新圖中對應點連邊,出線段數的節點是自上向下連邊的。
而後對於虛樹上的每一個節點 u,以它爲 lca 的點對就是全部 u 的兩兩子樹之間連邊,那麼咱們枚舉 u 的兒子 son,而後就是這個 son 的子樹向 u 的其餘兒子的子樹連邊,注意在 DFS 序中,「son 的子樹」是一段區間,「其餘兒子的子樹」是兩段區間,那麼就是線段樹上的 logn 個區間向 2logn 的區間兩兩連邊,咱們能夠建一個輔助點,把邊數變成 log 而不是 log2 的。除此以外,u 自己須要向它的子樹連邊,它的子樹也要向 u 連邊。注意這一段說的連邊的權值都是 u 在 Trie 樹上的深度。而且提醒一下在建輔助點的時候只須要把入邊的權值設成深度,出邊不須要再設了,不然在跑最短路時會重複計算這個代價。
這題呀,就是不知不覺就把代碼寫成 10K 了。。。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <queue> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 100010 #define maxnode 1000010 #define maxm 6000380 #define maxlog 17 #define ool (1ll << 60) #define LL long long namespace NG { int ToT, val[maxnode]; int iRt[maxn], oRt[maxn]; struct Seg_info { int l, r, lc, rc; Seg_info() {} Seg_info(int _1, int _2, int _3, int _4): l(_1), r(_2), lc(_3), rc(_4) {} } info[maxnode]; int m, head[maxnode], nxt[maxm], to[maxm], dist[maxm]; void init() { ToT = 0; memset(val, 0, sizeof(val)); m = 0; memset(head, 0, sizeof(head)); return ; } void AddEdge(int a, int b, int c) { // printf("NG::AddEdge(%d -> %d : %d)\n", a, b, c); to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m; return ; } int iseg_node(int o, int pos) { if(info[o].l == info[o].r) return o; int mid = info[o].l + info[o].r >> 1; if(pos <= mid) { if(!info[o].lc) { info[o].lc = ++ToT; info[info[o].lc] = Seg_info(info[o].l, mid, 0, 0); AddEdge(info[o].lc, o, 0); } return iseg_node(info[o].lc, pos); } if(!info[o].rc) { info[o].rc = ++ToT; info[info[o].rc] = Seg_info(mid + 1, info[o].r, 0, 0); AddEdge(info[o].rc, o, 0); } return iseg_node(info[o].rc, pos); } int oseg_node(int o, int pos) { if(info[o].l == info[o].r) return o; int mid = info[o].l + info[o].r >> 1; if(pos <= mid) { if(!info[o].lc) { info[o].lc = ++ToT; info[info[o].lc] = Seg_info(info[o].l, mid, 0, 0); AddEdge(o, info[o].lc, 0); } return oseg_node(info[o].lc, pos); } if(!info[o].rc) { info[o].rc = ++ToT; info[info[o].rc] = Seg_info(mid + 1, info[o].r, 0, 0); AddEdge(o, info[o].rc, 0); } return oseg_node(info[o].rc, pos); } int cIntv, Intv[maxn]; void getintv(int o, int ql, int qr) { if(!o) return ; if(ql <= info[o].l && info[o].r <= qr) Intv[++cIntv] = o; else { int mid = info[o].l + info[o].r >> 1; if(ql <= mid) getintv(info[o].lc, ql, qr); if(qr > mid) getintv(info[o].rc, ql, qr); } return ; } void GetIntv(int o, int ql, int qr) { cIntv = 0; if(ql > qr) return ; getintv(o, ql, qr); return ; } struct Node { int u; LL d; Node() {} Node(int _, LL __): u(_), d(__) {} bool operator < (const Node& t) const { return d > t.d; } }; priority_queue <Node> Q; bool vis[maxnode]; LL d[maxnode]; void ShortPath(int s) { memset(vis, 0, sizeof(vis)); for(int i = 1; i <= ToT; i++) d[i] = ool; d[s] = 0; Q.push(Node(s, 0)); while(!Q.empty()) { int u = Q.top().u; Q.pop(); // printf("(u)%d ", u); if(vis[u]) continue; vis[u] = 1; for(int e = head[u]; e; e = nxt[e]) if(d[to[e]] > d[u] + dist[e] + val[to[e]]) { d[to[e]] = d[u] + dist[e] + val[to[e]]; if(!vis[to[e]]) Q.push(Node(to[e], d[to[e]])); } } return ; } } struct Edge { int from, to, dist, tnode; Edge() {} Edge(int _1, int _2, int _3, int _4): from(_1), to(_2), dist(_3), tnode(_4) {} } es[maxn]; struct Graph { int m, hto[maxn], nto[maxn], hfr[maxn], nfr[maxn]; void init() { m = 0; memset(hto, 0, sizeof(hto)); memset(hfr, 0, sizeof(hfr)); return ; } void AddEdge(int a, int b, int c, int d) { es[++m] = Edge(a, b, c, d); nto[m] = hto[a]; hto[a] = m; nfr[m] = hfr[b]; hfr[b] = m; return ; } } G; struct Tree { int n, m, head[maxn], nxt[maxn], to[maxn]; int dep[maxn], Log[maxn<<1], mnp[maxlog][maxn<<1], pos[maxn], clo; void init() { m = 0; memset(head, 0, sizeof(head)); clo = 0; return ; } void AddEdge(int a, int b) { to[++m] = b; nxt[m] = head[a]; head[a] = m; return ; } void build(int u) { mnp[0][++clo] = u; pos[u] = clo; for(int e = head[u]; e; e = nxt[e]) { dep[to[e]] = dep[u] + 1; build(to[e]); mnp[0][++clo] = u; } return ; } void rmq_init() { Log[1] = 0; for(int i = 2; i <= clo; i++) Log[i] = Log[i>>1] + 1; for(int j = 1; (1 << j) <= clo; j++) for(int i = 1; i + (1 << j) - 1 <= clo; i++) { int a = mnp[j-1][i], b = mnp[j-1][i+(1<<j-1)]; mnp[j][i] = dep[a] < dep[b] ? a : b; } return ; } int lca(int a, int b) { int l = pos[a], r = pos[b]; if(l > r) swap(l, r); int t = Log[r-l+1]; a = mnp[t][l]; b = mnp[t][r-(1<<t)+1]; return dep[a] < dep[b] ? a : b; } int cdist(int a, int b) { return dep[a] + dep[b] - (dep[lca(a,b)] << 1); } } tree; bool cmp(int a, int b) { return tree.pos[a] < tree.pos[b]; } struct VTree { int n, m, head[maxn], nxt[maxn], to[maxn], dist[maxn]; int nodes[maxn], iN[maxn], ci, oN[maxn], co; int dl[maxn], dr[maxn], clo; int iRt, oRt, buff[maxn], cbuff; void init() { m = 0; memset(head, 0, sizeof(head)); return ; } void AddEdge(int a, int b, int c) { to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m; return ; } void build(int u) { dl[u] = ++clo; for(int e = head[u]; e; e = nxt[e]) build(to[e]); dr[u] = clo; return ; } void AddEdges(int u) { // printf("AddEdgessssssssss(%d) [%d, %d] %d\n", u, dl[u], dr[u], NG::m); NG::GetIntv(iRt, dl[u], dl[u]); if(NG::cIntv) { int x = NG::Intv[1]; NG::GetIntv(oRt, dl[u], dr[u]); // printf("[%d, %d]: %d\n", dl[u], dr[u], NG::cIntv); if(NG::cIntv) { for(int i = 1; i <= NG::cIntv; i++) { NG::AddEdge(x, NG::Intv[i], tree.dep[u]); // printf("HERE@@@@@@@ %d -> %d : %d\n", x, NG::Intv[i], tree.dep[u]); } } } NG::GetIntv(oRt, dl[u], dl[u]); if(NG::cIntv) { int x = NG::Intv[1]; NG::GetIntv(iRt, dl[u] + 1, dr[u]); if(NG::cIntv) { for(int i = 1; i <= NG::cIntv; i++) { NG::AddEdge(NG::Intv[i], x, tree.dep[u]); // printf("HERE@@@@@@@ %d -> %d : %d | we are here %d\n", NG::Intv[i], x, tree.dep[u], u); } } } for(int e = head[u]; e; e = nxt[e]) { NG::GetIntv(iRt, dl[to[e]], dr[to[e]]); if(NG::cIntv) { cbuff = NG::cIntv; for(int i = 1; i <= cbuff; i++) buff[i] = NG::Intv[i]; } else continue; int newnode = 0; NG::GetIntv(oRt, dl[u] + 1, dl[to[e]] - 1); if(NG::cIntv) { newnode = ++NG::ToT; for(int i = 1; i <= cbuff; i++) NG::AddEdge(buff[i], newnode, tree.dep[u]); for(int i = 1; i <= NG::cIntv; i++) NG::AddEdge(newnode, NG::Intv[i], 0); } NG::GetIntv(oRt, dr[to[e]] + 1, dr[u]); if(NG::cIntv) { if(newnode) for(int i = 1; i <= NG::cIntv; i++) NG::AddEdge(newnode, NG::Intv[i], 0); else { newnode = ++NG::ToT; for(int i = 1; i <= cbuff; i++) NG::AddEdge(buff[i], newnode, tree.dep[u]); for(int i = 1; i <= NG::cIntv; i++) NG::AddEdge(newnode, NG::Intv[i], 0); } } } /*printf("(u)%d: ", u); for(int e = head[u]; e; e = nxt[e]) printf("%d ", to[e]); printf("to[e] ends\n");*/ for(int e = head[u]; e; e = nxt[e]) AddEdges(to[e]); // printf("return to %d [%d, %d]\n", u, dl[u], dr[u]); return ; } void G_build(int u) { init(); n = ci = co = 0; for(int e = G.hfr[u]; e; e = G.nfr[e]) iN[++ci] = e, nodes[++n] = es[e].tnode; for(int e = G.hto[u]; e; e = G.nto[e]) oN[++co] = e, nodes[++n] = es[e].tnode; if(!ci || !co) return ; sort(nodes + 1, nodes + n + 1, cmp); int t = n; for(int i = 1; i < t; i++) nodes[++n] = tree.lca(nodes[i], nodes[i+1]); sort(nodes + 1, nodes + n + 1, cmp); n = unique(nodes + 1, nodes + n + 1) - nodes - 1; for(int i = 1; i < n; i++) { int a = nodes[i], b = nodes[i+1], c = tree.lca(a, b); AddEdge(c, b, tree.cdist(c, b)); } clo = 0; build(nodes[1]); NG::info[iRt = NG::iRt[u] = ++NG::ToT] = NG::Seg_info(1, clo, 0, 0); NG::info[oRt = NG::oRt[u] = ++NG::ToT] = NG::Seg_info(1, clo, 0, 0); // printf("G_build(%d) %d %d %d\n", u, n, clo, nodes[1]); for(int i = 1; i <= ci; i++) { int u = NG::iseg_node(iRt, dl[es[iN[i]].tnode]); NG::AddEdge(iN[i], u, 0); } for(int i = 1; i <= co; i++) { int u = NG::oseg_node(oRt, dl[es[oN[i]].tnode]); NG::AddEdge(u, oN[i], 0); } AddEdges(nodes[1]); return ; } } vtree; LL Ans[maxn]; void work() { G.init(); tree.init(); int n = read(), m = read(), k = read(); // printf("%d %d %d\n", n, m, k); for(int i = 1; i <= m; i++) { int a = read(), b = read(), c = read(), d = read(); G.AddEdge(a, b, c, d); } NG::init(); NG::ToT = m; for(int i = 1; i <= G.m; i++) NG::val[i] = es[i].dist; int Start = ++NG::ToT; for(int e = G.hto[1]; e; e = G.nto[e]) NG::AddEdge(Start, e, 0); for(int i = 1; i < k; i++) { int a = read(), b = read(); read(); tree.AddEdge(a, b); } tree.build(1); tree.rmq_init(); for(int i = 1; i <= n; i++) vtree.G_build(i); // printf("ToT: %d %d\n", NG::ToT, NG::m); NG::ShortPath(Start); for(int i = 1; i <= n; i++) Ans[i] = ool; for(int i = 1; i <= G.m; i++) Ans[es[i].to] = min(Ans[es[i].to], NG::d[i]); for(int i = 2; i <= n; i++) printf("%lld\n", Ans[i]); return ; } int main() { int T = read(); while(T--) work(); return 0; }