初见安~这里是传送门:洛谷P1967
题目描述
AA国有nn座城市,编号从 11到nn,城市之间有 mm 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 qq 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入格式:
第一行有两个用一个空格隔开的整数n,mn,m,表示 AA 国有nn 座城市和 mm 条道路。
接下来 mm行每行33个整数 x, y, zx,y,z,每两个整数之间用一个空格隔开,表示从 xx号城市到yy号城市有一条限重为 zz 的道路。注意: xx 不等于 yy,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。
输出格式:
共有 qq 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1−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\%30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,0000<n<1,000,0<m<10,000,0<q<1,000;
对于 60\%60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,0000<n<1,000,0<m<50,000,0<q<1,000;
对于 100\%100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,0000<n<10,000,0<m<50,000,0<q<30,000,0≤z≤100,000。
题解
首先一个图,给你两个点,让你找到一条路径使得路径上最小的边权最大。。直接对于每条路径找很麻烦,但是对于整个图来说,使其最小边权最大化,就很容易了——求最大生成树就行了。这一步操作想必是没有问题的。并且为了求出来过后我们还要接着操作,我们需要得到生成的所有边并存图,所以只能用Kruskal求最大生成树。
在此基础上,每两点之间的路径就唯一了。接下来我们要维护任意两点直接的路径上的最小边权,一看就是要用到LCA的。这里各位可以选择直接用倍增维护,写起来也是比较简单的【but我写不惯……】。这里我是采用的树剖+线段树来维护的,所以代码量看起来比较长【事实上都是些模板操作……】边化点的详细操作树剖传送门里有。
然后就没有了。【??!是不是思路说白了就两步,很简单?
至于判定无解-1,直接在Kruskal找到了各个点所在的并查集的基础上询问祖先节点是否相同即可。
可能写倍增不用考虑这么多【因为没有看到题解有人声明过这个细节】,但是还有一个极大的坑——最后一组毒瘤数据,我的代码爆了MLE,与此同时在bzoj的入门OJ上爆了RE。找了好久的原因才终于意识到——如果给你的图天生就不连通,而货车要运输的两点在同一个连通块呢?意思就是,你如果dfs标记只标记一次,那么你只会处理完其中一个连通块,剩下的都没有标记,编号都是0,以至于即便能到达,相连的边被选中了的,你也会因为树剖dfn标记异常而在查询的时候while卡爆。所以——要开一个for来确保标记了整个图……QAQ
下面上代码和详解!!!
#include<bits/stdc++.h>
#define maxn 10005
#define maxm 50005
using namespace std;
int read()
{
register int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
int n, m;
struct node
{
int u, v, w;
bool operator < (const node &x) const
{
return x.w < w;
}
}g[maxm];
int fa[maxn];
int get(int x)
{
if(fa[x] == x) return x;
return fa[x] = get(fa[x]);
}
struct edge
{
int to, w, nxt;
edge(){}
edge(int tt, int ww, int nn)
{
to = tt, w = ww, nxt = nn;
}
}e[maxn << 1];
int k = 0, head[maxn];
void add(int u, int v, int w)
{
e[k] = edge(v, w, head[u]);
head[u] = k++;
}
int dep[maxn], size[maxn], son[maxn], val[maxn], fa_[maxn];
void dfs_1(int u)//树剖初始化1
{
size[u] = 1;
for(register int i = head[u]; ~i; i = e[i].nxt)
{
register int v = e[i].to, w = e[i].w;
if(v == fa_[u]) continue;
dep[v] = dep[u] + 1;
fa_[v] = u;
dfs_1(v);
size[u] += size[v];
if(size[v] > size[son[u]]) son[u] = v;
}
}
int top[maxn], dfn[maxn], tot = 0;
void dfs_2(int u, int tp)//树剖初始化2
{
top[u] = tp;
dfn[u] = ++tot;
if(son[u]) dfs_2(son[u], tp);
for(register int i = head[u]; ~i; i = e[i].nxt)
{
register int v = e[i].to, w = e[i].w;
if(v != fa_[u] && v != son[u]) dfs_2(v, v);
val[dfn[v]] = w;
}
}
int road[maxn << 2];
void build(int p, int l, int r)//线段树建树
{
if(l == r)
{
road[p] = val[l];
return;
}
register int mid = l + r >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
road[p] = min(road[p << 1], road[p << 1 | 1]);
}
int ask(int p, int l, int r, int ls, int rs)//线段树查询
{
if(ls <= l && r <= rs)
return road[p];
}
register int mid = l + r >> 1, ans = 1 << 30;
if(ls <= mid) ans = min(ans, ask(p << 1, l, mid, ls, rs));
if(rs > mid) ans = min(ans, ask(p << 1 | 1, mid + 1, r, ls, rs));
return ans;
}
void work(int u, int v)//树剖基本操作
{
register int ans = 1 << 30;
register int tmp1 = u, tmp2 = v;
while(top[u] != top[v])
{
if(dep[top[u]] > dep[top[v]]) swap(u, v);
ans = min(ans, ask(1, 1, n, dfn[top[v]], dfn[v]));
v = fa_[top[v]];
}
if(dep[u] > dep[v]) swap(u, v);
ans = min(ans, ask(1, 1, n, dfn[son[u]], dfn[v]));
printf("%d\n", ans);
}
int main()
{
n = read(), m = read();
for(register int i = 1; i <= n; i++)
fa[i] = i;
for(register int i = 1; i <= m; i++)
g[i].u = read(), g[i].v = read(), g[i].w = read();
sort(g + 1, g + 1 + m);
memset(head, -1, sizeof head);
register int cnt = 0;//最大生成树开始
for(register int i = 1; i <= m; i++)
{
register int u = g[i].u, v = g[i].v, w = g[i].w;
if(get(u) == get(v)) continue;
fa[get(u)] = get(v);
add(u, v, w);
add(v, u, w);
cnt++;
if(cnt == n - 1) break;//省时处理
}
for(int i = 1; i <= n; i++)//这里一定要开for!!!QAQ
if(!size[i]) dfs_1(i);
for(int i = 1; i <= n; i++)//这里也一定要开for!!!QAQ
if(!top[i]) dfs_2(i, i);
val[1] = 1 << 30;//赋值
build(1, 1, n);
register int q, u, v;
q = read();
while(q--)
{
u = read(), v = read();
if(get(u) != get(v))//判定不连通情况
{
puts("-1");continue;
}
work(u, v);
}
}
真的佩服自己有耐心写这么长的代码还debug了这么久……
迎评:)
——End——