[LOJ#2270][BZOJ4912][SDOI2017]天才黑客
[LOJ#2270][BZOJ4912][SDOI2017]天才黑客
試題描述
SD0062 號選手小 Q 同學為了偷到 SDOI7012 的試題,利用高超的黑客技術潛入了 SDOI 出題組的內聯網的中央控制系統,然而這個內聯網除了配備有中央控制系統,還為內聯網中的每條單向網線設定了特殊的通信口令,這里通信口令是一個字符串,不同網線的口令可能不同。這讓小 Q 同學感覺有些棘手, 不過這根本難不倒他,很快他就分析出了整個內聯網的結構。
內聯網中有?n?個節點(從?1?到?n?標號)和?m?條單向網線,中央控制系統在第?1?個節點上,每條網線單向連接內聯網中的某兩個節點,從?1?號節點出發經過若干條網線總能到達其他任意一個節點。每個節點都可以運行任意的應用程序,應用程序會攜帶一條通信口令,當且僅當程序的口令與網線的口令相同時,程序才能通過這條網線到達另一端的節點繼續運行,并且通過每條網線都需要花費一定的時間。
每個應用程序可以在任意一個節點修改通信口令,修改通信口令花費的時間可以忽略不計,但是為了減小修改量,需要先調用一個子程序來計算當前程序的口令和網線的口令的最長公共前綴(記其長度為?len),由于獲取網線的口令的某個字符會比較耗時,調用一次這個子程序需要花費?len?個單位時間。
除此之外,小 Q 同學還在中央控制系統中發現了一個字典,每條網線的口令都是字典中的某個字符串。具體來說,這個字典是一棵?k?個節點(從?1?到?k?標號)的有根樹,其中根是第?1?個節點,每條邊上有一個字符,字符串?S?在字典中當且僅當存在某個點?u?使得從根節點出發往下走到?u?的這條路徑上的字符順次拼接構成?S。
現在小 Q 同學在?1?號節點同時開啟了?n?1?個應用程序,這些應用程序同時運行且互不干擾,每個程序的通信口令都為空,他希望用最短的時間把這些程序分別發送到其他節點上,你需要幫小 Q 同學分別計算出發送到第?i(=2,3,…,n)?個節點的程序完成任務的最短時間。
輸入
第一行是一個正整數?T,表示測試數據的組數,
對于每組測試數據,
第一行是三個整數?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?為根的有根樹,并且每個點連出去的邊上的字符互不相同。
輸出
對于每組測試數據,輸出?n?1?行,第?i?行表示發送到第?i+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 longnamespace 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; }?
轉載于:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/7133745.html
總結
以上是生活随笔為你收集整理的[LOJ#2270][BZOJ4912][SDOI2017]天才黑客的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL SERVER 中如何用脚本管理作
- 下一篇: case when then else