洛谷P1967 [Noip2013]货车运输
2017-08-21 19:16
441 查看
题目描述
A 国有n座城市,编号从1 到n,城市之间有m条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有q辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。输入输出格式
输入格式
输入文件名为truck.in。输入文件第一行有两个用一个空格隔开的整数n,m,表示 A 国有n座城市和m条道路。 接下来m行每行3个整数x,y,z,每两个整数之间用一个空格隔开,表示从x号城市到y号城市有一条限重为z的道路。注意: x不等于y,两座城市之间可能有多条道路 。
接下来一行有一个整数q,表示有q辆货车需要运货。
接下来q行,每行两个整数x,y,之间用一个空格隔开,表示一辆货车需要从x城市运输货物到y城市,注意:x不等于y。
输出格式
输出文件名为truck.out。输出共有q行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出−1。
输入输出样例
输入样例#1
4 31 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
输出样例#1
3-1
3
说明
对于30%的数据 0<n<1,000,0<m<10,000,0<q<1,000对于60%的数据 0<n<1,000,0<m<50,000,0<q<1,000
对于100%的数据 0<n<10,000,0<m<50,000,0<q<30,000,0≤z≤100,000
原题地址
洛谷P1967分析 最大生成树森林 + 树链剖分
题目的大意就是给出无向图中的q对点,要求找出连接每对点的一种路径方案,使得每条路径上边权的最小值最大,并输出这个最小值。因为这是无向图,于是我们做一遍最大生成树,这样每对点的答案就为生成树路径上边权的最小值。
简单用反证法证明一下:
假设两点间存在一条边不在生成树上,并且边权大于它们生成树路径上边权的最小值,那么按照Kruskal算法的贪心思想,我们肯定会根据边权优先选择这条边作为生成树上的边而不选择边权为最小值的那条边,这与之前的假设矛盾,因此我们得到两点的生成树路径上每条边的边权肯定都是尽量大的。
于是问题就被转化:给出一棵树,求树上两点路径上边权的最小值。
这可以用倍增LCA算,但我写的是树链剖分(感觉好像没什么人用)。
主要注意下这张图可能是不连通的,因此实际上应该是最大生成树森林,则不在同一棵树上即输出−1,这可以直接用并查集判断;并且我们要对森林中的每一棵树都剖分一遍(记一个bool数组表示点是否被访问过,然后每找到一个没被访问过的点就以这个点为根遍历出一棵树)。
另外剖分的是边权而不是点权,所以我们进行转化,把边权的信息存储在所连两点中深度较大的那一个点上,然后在询问时不查询深度最小的点即可。
代码
(有点小长……)#include <iostream> #include <cstdio> #include <algorithm> #define sL (s << 1) #define sR (s << 1 | 1) using namespace std; const int Maxn = 0x3f3f3f3f; const int N = 1e4 + 5, M = 1e5 + 5; int dep , sze , fa , idx , top , son , pos ; int sol , val[M], f ; bool vis ; int n, m, Q, P; char frd[M], *hed = frd + M; char fwt[M << 3], *opt = fwt; const char *tal = hed; inline char nxtChar() { if (hed == tal) fread(frd, 1, M, stdin), hed = frd; return *hed++; } inline int get() { char ch; int res = 0; while ((ch = nxtChar()) < '0' || ch > '9'); res = ch ^ 48; while ((ch = nxtChar()) >= '0' && ch <= '9') res = (res << 3) + (res << 1) + (ch ^ 48); return res; } inline void put(int x) { if (x > 9) put(x / 10); *opt++ = x % 10 + 48; } struct point {int l, r, w;}a[M]; struct Edge {int to, cst; Edge *nxt;}p[M], *T = p, *lst ; inline bool cmp(const point &x, const point &y) {return x.w > y.w;} inline void AddEdge(const int &x, const int &y, const int &z) { (++T)->nxt = lst[x]; lst[x] = T; T->to = y; T->cst = z; (++T)->nxt = lst[y]; lst[y] = T; T->to = x; T->cst = z; } inline int Find(const int &x) { if (f[x] != x) f[x] = Find(f[x]); return f[x]; } inline void Dfs1(const int &x, const int &F) { dep[x] = dep[F] + 1; fa[x] = F; sze[x] = 1; vis[x] = true; for (Edge *e = lst[x]; e; e = e->nxt) { int y = e->to; if (y == F) continue; sol[y] = e->cst; Dfs1(y, x); sze[x] += sze[y]; if (sze[y] > sze[son[x]]) son[x] = y; } } inline void Dfs2(const int &x) { int y; if (son[x]) { top[y = son[x]] = top[x]; idx[pos[y] = ++P] = y; Dfs2(y); } for (Edge *e = lst[x]; e; e = e->nxt) if (!top[y = e->to]) { top[y] = y; idx[pos[y] = ++P] = y; Dfs2(y); } } inline int Min(const int &x, const int &y) {return x < y ? x : y;} inline void Push(const int &s) {val[s] = Min(val[sL], val[sR]);} inline void Build(const int &s, const int &l, const int &r) { if (l == r) return (void)(val[s] = sol[idx[l]]); int mid = l + r >> 1; Build(sL, l, mid); Build(sR, mid + 1, r); Push(s); } inline int Query(const int &s, const int &l, const int &r, const int &x, const int &y) { if (l == x && r == y) return val[s]; int mid = l + r >> 1; if (y <= mid) return Query(sL, l, mid, x, y); else if (x > mid) return Query(sR, mid + 1, r, x, y); else return Min(Query(sL, l, mid, x, mid), Query(sR, mid + 1, r, mid + 1, y)); } inline void Swap(int &x, int &y) {x ^= y; y ^= x; x ^= y;} inline void CkMin(int &x, const int &y) {if (x > y) x = y;} inline int PaQuery(int x, int y) { int res = Maxn; while (top[x] != top[y]) { if (dep[top[x]] < dep[top[y]]) Swap(x, y); CkMin(res, Query(1, 1, n, pos[top[x]], pos[x])); x = fa[top[x]]; } if (x == y) return res; if (dep[x] < dep[y]) Swap(x, y); return Min(res, Query(1, 1, n, pos[y] + 1, pos[x])); } int main() { n = get(); m = get(); for (int i = 1; i <= m; ++i) { a[i].l = get(); a[i].r = get(); a[i].w = get(); } for (int i = 1; i <= n; ++i) f[i] = i; sort(a + 1, a + m + 1, cmp); int K = 0; for (int i = 1; i <= m; ++i) { int tx = Find(a[i].l), ty = Find(a[i].r); if (tx != ty) { f[tx] = ty; K++; AddEdge(a[i].l, a[i].r, a[i].w); } if (K == n - 1) break; } Q = get(); int x, y; for (int i = 1; i <= n; ++i) if (!vis[i]) { Dfs1(i, 0); idx[pos[i] = ++P] = top[i] = i; Dfs2(i); } Build(1, 1, n); for (int i = 1; i <= Q; ++i) { x = get(); y = get(); if (Find(x) != Find(y)) *opt++ = '-', *opt++ = '1'; else put(PaQuery(x, y)); *opt++ = '\n'; } fwrite(fwt, 1, opt - fwt, stdout); }
相关文章推荐
- 洛谷—— P1967 货车运输 || COGS——C 1439. [NOIP2013]货车运输
- 洛谷P1967 [NOIP2013提高组Day1T2]货车运输
- 洛谷 P1967 Vijos P1843 CODE[VS] P3287 [NOIP2013 D1T3] 货车运输
- 【洛谷P1967】【NOIP2013】货车运输
- [NOIP2013] 提高组 洛谷P1967 货车运输
- 洛谷 P1967 [NOIP2013 D1T3] 货车运输
- 洛谷1967货车运输 即 NOIP2013 DAY1 T3
- 【洛谷1967】【NOIP2013】货车运输
- 洛谷 1967 [NOIP2013] 货车运输 最大生成树+倍增
- 【CJOJ1090】【洛谷1967】【NOIP2013】货车运输
- 【洛谷】1967 [noip2013]货车运输 最小生成树+LCA
- [P1967][NOIP2013]货车运输
- 【CJOJ1090】【洛谷1967】【NOIP2013】货车运输
- 洛谷 P1967 货车运输
- 水题笔记:noip2013 货车运输 [LCA]
- NOIP 2013 【货车运输】
- NOIP提高组 2013货车运输
- NOIP2013 D1T3 货车运输 倍增LCA OR 并查集按秩合并
- [NOIP2013]货车运输
- noip2013货车运输