zoukankan      html  css  js  c++  java
  • 「SDOI2017」天才黑客「优化建图最短路」

    题意

    给定一个(n)个点(m)条边的有向图和一个(k)个结点的字典树,每条边有一个时间花费(c)和口令,口令用字典树上一个结点表示。走一条边((u,v))的代价是这条边的时间花费加上你在(u)点口令和该边口令的lcp长度,并且到达(v)点后口令变成了该边口令。对于(i = 2,3,...,n),你需要求出若你起初在(1)号结点且口令为空,到(i)的最小代价。

    (n, mleq 5 imes 10^4,k leq 2 imes 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
    ", 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
    ", ans);
    		}
    	}
    	return 0;
    }
    

    

  • 相关阅读:
    [板子]用线段树解决ST表问题
    [POJ2528]Mayor's posters(离散化+线段树)
    [板子]Kruskal
    [板子]segTree
    js实现工具函数中groupBy数据分组
    关于爬虫
    jsencrypt vue相关的rsa加密
    less 循环模拟sass的for循环效果
    vue 自动生成菜单
    vue中form 表单常用校验封装(async-validator)
  • 原文地址:https://www.cnblogs.com/hongzy/p/12805624.html
Copyright © 2011-2022 走看看