您的位置:首页 > 其它

NOIP 2013 Senior 3 - 货车运输

2017-07-20 11:19 267 查看
Description

A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车 在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

Input

第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。

接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。 注意:x 不等于 y,两座城市之间可能有多条道路。

接下来一行有一个整数 q,表示有 q 辆货车需要运货。

接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。

Output

输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。

Sample Input

4 3

1 2 4

2 3 3

3 1 1

3

1 3

1 4

1 3

Sample Output

3

-1

3

Data Constraint

对于 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。

这个题给你的第一感觉是什么?搜索?变形SPFA?很明显的是,哪怕这两个算法的时间复杂度都降低到接近O(n)了,也会超时,所以我们还是从数据规模入手。发现30%的数据和60%的数据很有玄机:只有边数的规模改变了,顶点和询问还是那么多。所以我们断定,这道题一定跟边的处理有关。

经过观察后发现,我们要求的路径是要求最短边最长,跟总长无关,也就是说每条边尽量长就可以了。考虑一个两点之间只有唯一路径的图,那它就是一棵树,最优答案就是唯一的答案。反过来,我们走出来的最优答案一定是一棵树。如果这棵树再加上几条边呢?就成了我们题目给的图了。因此,要让这棵树最优,那么这棵树的每条边就应该最优。所以这棵树是一棵最大生成树,因为不是最大生成树的树答案不比最大生成树的答案好。

好了,现在我们用Kruskal算法拿到了一棵(无根)树。那么要求两点之间路径怎么求呢?爆搜?= =很明显,通常做法都是把无根树转换为有根树,即先随便找一个点深搜一次。现在要求两点之间的路径就需要找最近公共祖先了。

可以保存每个点的depth,然后通过最近公共祖先的depth和当前点的depth计算出要走的步数,边走边统计。因此还可以使用倍增加速。至此,思路已经基本完善。

但是,题目给定的图可能包含多个互不连通的子图,因此,我们得到的是一个森林。所以在写代码的时候还要注意处理不在同一棵树上的情况,例如,要dfs所有点。

参考代码

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
using std::cin;
using std::cout;
using std::endl;
typedef int INT;
inline INT readIn()
{
bool minus = false;
INT a = 0;
char ch = getchar();
while (!(ch == '-' || ch >= '0' && ch <= '9')) ch = getchar();
if (ch == '-')
{
minus = true;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
a *= 10;
a += ch;
a -= '0';
ch = getchar();
}
if (minus) a = -a;
return a;
}

struct DS
{
std::vector<INT> parent;

DS(INT size) : parent(size + 1)
{
for (int i = 1; i <= size; i++)
{
parent[i] = i;
}
}

INT find(INT x)
{
if (parent[x] == x) return x;
return parent[x] = find(parent[x]);
}
INT unite(INT x, INT y)
{
INT px = find(x);
INT py = find(y);
parent[py] = px;
}
INT judge(INT x, INT y)
{
return find(x) == find(y);
}
};

const INT maxn = 10005;
INT n, m;
struct E
{
INT from;
INT to;
INT cost;
E(INT from = 0, INT to = 0, INT cost = 0) : from(from), to(to), cost(cost)
{
}

bool operator< (const E& b) const
{
return cost > b.cost;
}
} e[50005];
INT k;

struct edge
{
INT to;
INT cost;
edge(INT to = 0, INT cost = 0) : to(to), cost(cost)
{

}
};
std::vector<std::vector<edge> > edges;

void kruskal(DS &ds)
{
edges.resize(n + 1);
INT count_ = 0;
for (int i = 0; i < m; i++)
{
if (!ds.judge(e[i].from, e[i].to))
{
ds.unite(e[i].from, e[i].to);
edges[e[i].from].push_back(edge(e[i].to, e[i].cost));
edges[e[i].to].push_back(edge(e[i].from, e[i].cost));
}
}
}

std::vector<INT> sequence(1);
std::vector<INT> vis;
std::vector<INT> depth;
std::vector<std::vector<INT> > stTable(1);
std::vector<std::vector<INT> > parent(1);
std::vector<std::vector<INT> > minLen(1);
void dfs(INT node = 1)
{
vis[node] = sequence.size();
sequence.push_back(node);
for (int i = 0; i < edges[node].size(); i++)
{
INT to = edges[node][i].to;
if (to == parent[0][node]) continue;
INT cost = edges[node][i].cost;

depth[to] = depth[node] + 1;
parent[0][to] = node;
minLen[0][to] = cost;
dfs(to);
sequence.push_back(node);
}
}
void ST()
{
INT N = sequence.size() - 1;
INT size = 0;
while (1 << (size + 1) <= N) size++;

stTable[0].resize(N + 1);
for (int i = 1; i <= N; i++)
{
stTable[0][i] = sequence[i];
}

for (int i = 1; i <= size; i++)
{
stTable.push_back(std::vector<INT>());
stTable.back().resize(N + 1);
for (int j = 1; j + (1 << i) - 1 <= N; j++)
{
INT x = stTable[i - 1][j];
INT y = stTable[i - 1][j + (1 << (i - 1))];
if (depth[stTable[i - 1][j]] < depth[stTable[i - 1][j + (1 << (i - 1))]])
{
stTable[i][j] = stTable[i - 1][j];
}
else
{
stTable[i][j] = stTable[i - 1][j + (1 << (i - 1))];
}
}
}

N = n;
size = 0;
while (1 << (size + 1) <= N) size++;
for (int i = 1; i <= size; i++)
{
parent.push_back(std::vector<INT>());
minLen.push_back(std::vector<INT>());
parent.back().resize(N + 1);
minLen.back().resize(N + 1);
for (int j = 1; j <= N; j++)
{
parent[i][j] = parent[i - 1][parent[i - 1][j]];
if (parent[i][j]) minLen[i][j] = std::min(minLen[i - 1][j], minLen[i - 1][parent[i - 1][j]]);
}
}
}
INT getMin(int& x, int& y)
{
INT l = vis[x];
INT r = vis[y];
if (l > r)
{
std::swap(l, r);
std::swap(x, y);
}
INT length = r - l + 1;
INT size = 0;
while (1 << (size + 1) <= length) size++;

if (depth[stTable[size][l]] < depth[stTable[size][r - (1 << size) + 1]])
return stTable[size][l];
else
return stTable[size][r - (1 << size) + 1];
}

void run()
{
n = readIn();
m = readIn();
for (int i = 0; i < m; i++)
{
e[i].from = readIn();
e[i].to = readIn();
e[i].cost = readIn();
}
std::sort(e, e + m);
DS ds(n);
kruskal(ds);
vis.resize(n + 1);
depth.resize(n + 1);
parent[0].resize(n + 1);
minLen[0].resize(n + 1);

for (int i = 1; i <= n; i++)
{
if (!vis[i]) dfs(i);
}

ST();

k = readIn();
while (k--)
{
INT from = readIn();
INT to = readIn();
if (ds.judge(from, to))
{
INT anc = getMin(from, to);
INT depthFrom = depth[from];
INT depthTo = depth[to];
INT depthAnc = depth[anc];
INT stepFrom = depthFrom - depthAnc;
INT stepTo = depthTo - depthAnc;

INT minVal = 0x7ffffff;

for (int i = parent.size() - 1; ~i; i--)
{
if (stepFrom & (1 << i))
{
minVal = std::min(minVal, minLen[i][from]);
from = parent[i][from];
}
}
for (int i = parent.size() - 1; ~i; i--)
{
if (stepTo & (1 << i))
{
minVal = std::min(minVal, minLen[i][to]);
to = parent[i][to];
}
}
printf("%d\n", minVal);
}
else
{
printf("-1\n");
}
}
}

int main()
{
run();
return 0;
}


写的比较复杂。要加强对基础算法的回顾,像这道题完全就是一堆算法模板拿来套。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: