• bzoj4489 [Jsoi2015]地铁线路 最短路


    题目传送门

    https://lydsy.com/JudgeOnline/problem.php?id=4489

    题解

    感觉又被骗了。看这道题的 AC 人数不多,以为是一道很好的题目。结果发现是一个非常一般的最短路啊。

    第一问的话,相当于同一个集合内的点可以瞬移,不需要花钱。于是可以每条线路建一个点,把每个站点向所在线路连边,边权为 (0);每条道路向站点连边,边权为 (1)。显然从 (S)(T) 的最短路就是第一问的答案。最短路可以通过 01bfs 实现。

    然后第二问的话,也可以继续建图来做。但是有一个更方便的做法。我们可以通过第一问得到的最短路给每一条线路和每个站点分层。然后对于同一层,枚举每一条线路,用这条线路上低一层的点来更新本层的点。(更新可以通过前后缀最大值来实现)


    代码如下。

    #include<bits/stdc++.h>
    #include<tr1/unordered_map>
    
    #define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
    #define dbg(...) fprintf(stderr, __VA_ARGS__)
    #define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
    #define fi first
    #define se second
    #define pb push_back
    
    template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
    template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;}
    
    typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
    
    template<typename I>
    inline void read(I &x) {
    	int f = 0, c;
    	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    	x = c & 15;
    	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    	f ? x = -x : 0;
    }
    
    const int N = 3e5 + 50000 + 7;
    const int M = 3e5 + 7;
    const int INF = 0x3f3f3f3f;
    
    int n, m, nod, S, T, hd, tl;
    int dis[N], p[N], mxt[N], mxt2[N], pre[N], suf[N];
    std::deque<int> q;
    std::tr1::unordered_map<std::string, int> mp;
    std::vector<int> v[N];
    char tmp[50];
    
    struct Edge { int to, ne; } g[N * 6]; int head[N], tot;
    inline void addedge(int x, int y) { g[++tot].to = y, g[tot].ne = head[x], head[x] = tot; }
    inline void adde(int x, int y) { addedge(x, y), addedge(y, x); }
    
    inline void build() {
    	for (int i = 1; i <= n; ++i) {
    		int len = v[i].size();
    		for (int j = 0; j < len; ++j) adde(i + m, v[i][j]);
    	}
    }
    
    inline void bfs() {
    	q.push_back(S);
    	memset(dis, -1, sizeof(dis)), dis[S] = 0;
    	while (!q.empty()) {
    		int x = q.front(); q.pop_front();
    		if (x <= m) {
    			for fec(i, x, y) if (!~dis[y]) dis[y] = dis[x] + 1, q.push_back(y);
    		} else for fec(i, x, y) if (!~dis[y]) dis[y] = dis[x], q.push_front(y);
    	}
    }
    
    inline bool cmp(const int &x, const int &y) { return dis[x + m] < dis[y + m]; }
    inline void work() {
    	build();
    	bfs();
    	for (int i = 1; i <= n; ++i) p[i] = i;
    	std::sort(p + 1, p + n + 1, cmp);
    	int r = 0;
    	while (r < n && dis[p[r + 1] + m] <= 0) ++r;
    	for (int i = 1; i <= n; ++i) {
    		int l = r + 1;
    		while (r < n && dis[p[r + 1] + m] == i) ++r;
    		for (int j = l; j <= r; ++j) {
    			int id = p[j], len = v[id].size();
    			pre[0] = suf[len + 1] = -INF;
    			for (int k = 0; k < len; ++k) pre[k + 1] = std::max(pre[k] + 1, dis[v[id][k]] == i - 1 ? mxt[v[id][k]] : -INF);
    			for (int k = len - 1; ~k; --k) suf[k + 1] = std::max(suf[k + 2] + 1, dis[v[id][k]] == i - 1 ? mxt[v[id][k]] : -INF);
    			for (int k = 0; k < len; ++k) if (dis[v[id][k]] == i) smax(mxt[v[id][k]], std::max(pre[k + 1], suf[k + 1]));
    		}
    	}
    	printf("%d
    ", dis[T]);
    	printf("%d
    ", mxt[T]);
    }
    
    inline void init() {
    	read(n), read(m);
    	for (int i = 1; i <= m; ++i) scanf("%s", tmp), mp[tmp] = i;
    	for (int i = 1; i <= n; ++i) {
    		int l;
    		read(l);
    		while (l--) scanf("%s", tmp), v[i].pb(mp[tmp]);
    	}
    	scanf("%s", tmp), S = mp[tmp];
    	scanf("%s", tmp), T = mp[tmp];
    }
    
    int main() {
    #ifdef hzhkk
    	freopen("hkk.in", "r", stdin);
    #endif
    	init();
    	work();
    	fclose(stdin), fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    解上三角矩阵和下三角矩阵方程的fortran程序
    用sublimetext写fortran程序
    fortran子程序传入可变数组要在module里实现
    ubuntu wineqq 输入中文显示方格的问题
    mathtype快捷键很方便+公式上边不显示问题的解决
    滚动字符小程序-python
    传递矩阵法求简支梁固有频率的近似解 --matlab程序
    explorer.exe总是重启导致打开的文件夹关闭
    python把数据分为训练部分和测试部分的简单实现
    用python批量删掉文件名中共同存在的字符
  • 原文地址:https://www.cnblogs.com/hankeke/p/bzoj4489.html
Copyright © 2020-2023  润新知