P1967 [NOIP2013 提高组] 货车运输 题解
作者:互联网
题目描述
A 国有 \(n\) 座城市,编号从 \(1\) 到 \(n\),城市之间有 \(m\) 条双向道路。每一条道路对车辆都有重量限制,简称限重。
现在有 \(q\) 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入格式
第一行有两个用一个空格隔开的整数 $ n,m$,表示 \(A\) 国有 $ n$ 座城市和 \(m\) 条道路。
接下来 \(m\) 行每行三个整数 \(x, y, z\),每两个整数之间用一个空格隔开,表示从 $x $ 号城市到 $ y $ 号城市有一条限重为 \(z\) 的道路。
注意: \(x \neq y\),两座城市之间可能有多条道路 。
接下来一行有一个整数 \(q\),表示有 \(q\) 辆货车需要运货。
接下来 \(q\) 行,每行两个整数 \(x,y\),之间用一个空格隔开,表示一辆货车需要从 \(x\) 城市运输货物到 \(y\) 城市,保证 \(x \neq y\)
输出格式
共有 \(q\) 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。
如果货车不能到达目的地,输出 \(-1\)。
样例输入
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
样例输出
3
-1
3
对于 \(100\%\) 的数据,\(1 \le n < 10^4\),\(1 \le m < 5\times 10^4\),$1 \le q< 3\times 10^4 $,\(0 \le z \le 10^5\)。
Kruskal 重构树板子题,但我不想动脑子,所以使用 Kruskal 求最大生成树 + 树链剖分维护边权 + 线段树维护静态最小值。跑最大生成树的原因显然,贪心思想跟路径远近无关的情况下应该尽量沿着边权大的走,但限制两个点之间的距离承重又是两条路之间最小的那个决定的,原因它在一棵生成树上,两点之间有且只有一条路。
说点细节,树剖维护边权是要在第一遍 dfs 时将边权换为子节点点权,然后在第二遍 dfs 针对点权建立正反映射。
还有,查询时最后是从 \([id[v]+1,id[u]]\) 加一,加一,加一,剩下地方不变,不变,不变。
原题构造出来的可能是一颗森林所以需要以所有并查集的父节点都跑一遍树剖。
Code.
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10,M=5e4+10,imax=INT_MAX;
int n,m,h[N],ne[N<<1],e[N<<1],w[N<<1],idx,p[N],dep[N],sz[N],fa[N],son[N],id[N],cnt,yl[N],top[N],val[N],q;
struct node
{
int u,v,w;
bool operator < (const node &o) const{
return w > o.w;
}
} pl[M];
void add(int u,int v,int c) {ne[++idx]=h[u],e[idx]=v,w[idx]=c,h[u]=idx;}
int find(int x) {if(p[x] != x) p[x]=find(p[x]); return p[x];}
void dfs1(int u,int father,int depth)
{
dep[u]=depth; sz[u]=1; fa[u]=father;
for(int i=h[u];~i;i=ne[i])
{
int j=e[i]; if(j == father) continue ;
val[j]=w[i]; dfs1(j,u,depth+1); sz[u]+=sz[j];
if(sz[son[u]] < sz[j]) son[u]=j;
}
}
void dfs2(int u,int t)
{
id[u]=++cnt; yl[cnt]=val[u]; top[u]=t;
if(! son[u]) return ; dfs2(son[u],t);
for(int i=h[u];~i;i=ne[i])
{
int j=e[i]; if(j == son[u] || j == fa[u]) continue ;
dfs2(j,j);
}
}
struct Seg
{
struct node {int l,r,mn;} tr[N<<2];
void pushup(int u) {tr[u].mn=min(tr[u<<1].mn,tr[u<<1|1].mn);}
void build(int u,int l,int r)
{
tr[u].l=l,tr[u].r=r,tr[u].mn=imax;
if(l == r) return tr[u].mn=yl[l],void();
int mid = l + r >> 1;
build(u<<1,l,mid); build(u<<1|1,mid+1,r);
pushup(u);
}
int query(int u,int l,int r)
{
if(l <= tr[u].l && tr[u].r <= r) return tr[u].mn;
int mid = tr[u].l + tr[u].r >> 1,res=imax;
if(l<=mid) res=min(res,query(u<<1,l,r)); if(r>mid) res=min(res,query(u<<1|1,l,r));
return res;
}
} st;
int query(int u,int v)
{
int res=imax;
while(top[u] != top[v])
{
if(dep[top[u]] < dep[top[v]]) swap(u,v);
res=min(res,st.query(1,id[top[u]],id[u]));
u=fa[top[u]];
}
if(dep[u] < dep[v]) swap(u,v);
res=min(res,st.query(1,id[v]+1,id[u]));
return res;
}
int main()
{
memset(h,-1,sizeof h); scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) p[i]=i;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&pl[i].u,&pl[i].v,&pl[i].w);
stable_sort(pl+1,pl+1+m);
for(int i=1;i<=m;i++)
{
int u=find(pl[i].u),v=find(pl[i].v);
if(u == v) continue ; p[u]=v;
add(pl[i].u,pl[i].v,pl[i].w);
add(pl[i].v,pl[i].u,pl[i].w);
}
for(int i=1;i<=n;i++)
{
if(p[i] != i) continue ; val[i]=imax;
dfs1(i,0,1); dfs2(i,i);
}
st.build(1,1,n);
scanf("%d",&q);
while(q -- )
{
int u,v; scanf("%d%d",&u,&v);
if(p[find(u)] != p[find(v)]) puts("-1");
else printf("%d\n",query(u,v));
}
return 0;
}
标签:10,le,idx,sz,int,题解,货车运输,son,P1967 来源: https://www.cnblogs.com/EastPorridge/p/16600581.html