A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。
现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
本题求一条路径,使得其在不超过限制重量的前提下,载货重量的最大价值(最小值最大化),我们可以考虑求最大生成树,运用贪心的思想,求解答案。
根据输入数据,是一颗无根树,我们想到树链剖分,结合线段树维护区间最小值
代码如下(示例):
struct DSU
{
int f[N];
void init(int n) { for (int i = 1; i <= n; i++) f[i] = i;}
int find(int u) { return u == f[u] ? u : f[u] = find(f[u]); }
int merge(int u,int v)
{
int t1,t2; t1 = find(u); t2 = find(v);
if (t1 != t2)
{
f[t2] = t1; return 1;
}
return 0;
}
}dsu;
struct seg
{
int l,r,mn;
}tree[N*4];
struct edg
{
int u,v,w;
};
struct edge
{
int v,w,ne;
}e[100005];
这里封装了并查集,用于求最小生成树。
void Kruskal()
{
dsu.init(n);
sort(el.begin(),el.end(),[] (const edg &pa,const edg &pb) { return pa.w > pb.w; });
int ct = 0;
for (auto [u,v,w] : el)
{
if (dsu.merge(u,v))
{
add(u,v,w); add(v,u,w);
ct++;
}
if (ct == n-1)
{
break;
}
}
}
先使用Kruskal求最大生成树(贪心思想),并建立新图,新图使用链式前向星。
void dfs1(int u,int f);
void dfs2(int u,int t);
int ls(int p);
int rs(int p);
void pushup(int p);
void buildTree(int p,int l,int r);
int query(int p,int l,int r);
结合树剖+线段树求区间最小值即可得到答案。
#include
using namespace std;
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
const int N = 10005;
struct DSU
{
int f[N];
void init(int n) { for (int i = 1; i <= n; i++) f[i] = i;}
int find(int u) { return u == f[u] ? u : f[u] = find(f[u]); }
int merge(int u,int v)
{
int t1,t2; t1 = find(u); t2 = find(v);
if (t1 != t2)
{
f[t2] = t1; return 1;
}
return 0;
}
}dsu;
struct seg
{
int l,r,mn;
}tree[N*4];
struct edg
{
int u,v,w;
};
struct edge
{
int v,w,ne;
}e[100005];
int depth[N],sz[N],son[N],fa[N],top[N],id[N],nw[N],ww[N],cnt;
int h[N],idx,n,m,q;
vector<edg> el;
void add(int u,int v,int w);
void Kruskal();
int query_mn(int u,int v);
void dfs1(int u,int f);
void dfs2(int u,int t);
int ls(int p);
int rs(int p);
void pushup(int p);
void buildTree(int p,int l,int r);
int query(int p,int l,int r);
int main()
{
IOS; cin >> n >> m;
memset(h,-1,sizeof(h));
for (int i = 1;i <= m; i++)
{
int u,v,w; cin >> u >> v >> w;
el.push_back({u,v,w});
}
Kruskal();
for (int i = 1; i <= n; i++)
{
if (sz[i]) continue;
dfs1(i,0);
}
for (int i = 1; i <= n; i++)
{
if (id[i]) continue;
dfs2(i,i);
}
buildTree(1,1,cnt);
cin >> q;
while (q--)
{
int l,r; cin >> l >> r;
if (l > r) swap(l,r);
cout << query_mn(l,r) << "\n";
}
return 0;
}
void add(int u,int v,int w)
{
e[idx] = {v,w,h[u]};
h[u] = idx++;
}
void Kruskal()
{
dsu.init(n);
sort(el.begin(),el.end(),[] (const edg &pa,const edg &pb) { return pa.w > pb.w; });
int ct = 0;
for (auto [u,v,w] : el)
{
if (dsu.merge(u,v))
{
add(u,v,w); add(v,u,w);
ct++;
}
if (ct == n-1)
{
break;
}
}
}
int query_mn(int u,int v)
{
if (dsu.find(u) != dsu.find(v)) return -1;
int mn = INT32_MAX;
while (top[u] != top[v])
{
if (depth[top[u]] < depth[top[v]]) swap(u,v);
mn = min(mn,query(1,id[top[u]],id[u]));
u = fa[top[u]];
}
if (u == v) return mn;
if (depth[u] < depth[v]) swap(u,v);
mn = min(mn,query(1,id[v]+1,id[u]));
return mn;
}
void dfs1(int u,int f)
{
depth[u] = depth[f] + 1;
sz[u] = 1; fa[u] = f;
for (int i = h[u]; ~i; i = e[i].ne)
{
int v = e[i].v;
if (v == f) continue;
ww[v] = e[i].w;
dfs1(v,u);
sz[u] += sz[v];
if (sz[son[u]] < sz[v]) son[u] = v;
}
}
void dfs2(int u,int t)
{
top[u] = t; id[u] = ++cnt; nw[cnt] = ww[u];
if (!son[u]) return;
dfs2(son[u],t);
for (int i = h[u]; ~i; i = e[i].ne)
{
int v = e[i].v;
if (v == fa[u] || v == son[u]) continue;
dfs2(v,v);
}
}
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void pushup(int p) { tree[p].mn = min(tree[ls(p)].mn,tree[rs(p)].mn); }
void buildTree(int p,int l,int r)
{
tree[p] = {l,r,nw[l]};
if (l == r) return;
int mid = l + r >> 1;
buildTree(ls(p),l,mid);
buildTree(rs(p),mid+1,r);
pushup(p);
}
int query(int p,int l,int r)
{
if (l <= tree[p].l && tree[p].r <= r) return tree[p].mn;
int mid = tree[p].l + tree[p].r >> 1,ans = INT32_MAX;
if (l <= mid) ans = min(ans,query(ls(p),l,r));
if (r > mid) ans = min(ans,query(rs(p),l,r));
return ans;
}