您的位置:首页 > 其它

洛谷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 3

1 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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息