to神犇:请自动跳过前x行的吐槽,因为吐槽内容可能只是一些极其无聊的bug和友情提示……
一道极其经典且相当BT的图论题,不过目前看来与运输计划相比似乎更仁慈一些。在你下定决心AC这道题之前,你首先要做好debug到怀疑人生的准备,最好准备一瓶眼药水以备不测,当然有一份画风清奇简约养眼的题解也是极好的……;其次,你要明白这道题的代码长度——不压缩180左右——所以要有足够的耐心,找一把舒适的椅子再好不过了;last but not the least,你要拥有足够的知识储备——图论+搜索=最小生成树+LCA(倍增或tarjan)+RMQ+DFS,建议是别抄模板(当心wa惨)。如果你确信已经具备了以上条件,请点击以下链接或继续阅读,完成AC之路的第一步——审题。友情提示:codevs上的数据略水(水了8个点……),洛谷数据和官方一致。
原题地址:https://www.luogu.org/problem/show?pid=1967
题目描述
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。
point.1 建树
显然给出的数据只能构造出一张无向图,可能有重边,可能不连通。直接从这张图上跑无法达到我们的目的,所以要跑一遍最大生成树,生成多棵无根树构成森林。实现时要另开一个邻接表存生成的树,双向边连接。另外要注意跑kruscal的时候get两个端点的祖先时注意进行路径压缩,防止超时。
point.2 DFS
枚举节点,理论上同一棵树上的每个节点都可以作为根,所以只要碰到没有搜过的点就来一把dfs,搜过的点都打上标记,这样可以遍历整个森林。dfs的目的有两个:
1.确定每一棵树中节点的父子关系,同时维护f[i][0](从第i个节点向上跳2^0步到达的节点,实际上就是father[i])和minp[i][0](从第i个节点向上跳2^0步走过的边中的最短边);
2. 遍历整棵树,求出每个节点的深度d[i]。
point.3 RMQ、LCA的初始化
具体算法就不啰嗦了,本质上和模板差不多,只不过这里是树上维护RMQ,写法上和区间RMQ不太一样(其实更好写了)。
point.4 求LCA同时维护答案
注意目的不在找到LCA,而是在找的过程中维护最小值,返回值也是这个最小值。
解题思路
都在上文里提到了,这里讲几个debug时碰到的蠢毙了的bug:
1. kruscal路径压缩时,fa[x]=get(fa[x])要放在return之前,否则会导致这一子程序变成回溯过程。结果……T了8个点够不够狠?
2. 最大生成树,所以要重载 < 。还有建边时要建双·向·边。
3. dfs时要先枚举而不能只从1开始搜……好吧这其实并不算bug……
4. 倍增初始化第二维最好至少要跑到15,没错真的有2^10+的深度……
5. 读入一对x/y后,先判断是否在同一棵树上,不在直接输出“-1”,否则跑LCA。
6. 数组要开够要开够要开够……不要忘了边数是m*2。
参考代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int num=0, num2=0,ans,mi,n,m;
int v[10005], v2[10005] ,fa[10005],d[10005],pre_len[10005],pre_num[10005];
int minp[10005][20], f[10005][20];
bool vis[10005];
struct mc
{
intx,y,l,ne;
booloperator <(const mc b) const
{
returnl>b.l;
}
}e[100005],p[100005];
void put(int x,int y,int l)
{
num++;
e[num].x=x;
e[num].y=y;
e[num].l=l;
e[num].ne=v[x];
v[x]=num;
}
int get(int x)
{
if(fa[x]==x) return x;
else
{
fa[x]=get(fa[x]);
returnget(fa[x]);
}
}
void put2(int x,int y,int l)
{
num2++;
p[num2].x=x;
p[num2].y=y;
p[num2].ne=v2[x];
p[num2].l=l;
v2[x]=num2;
}
void dfs(int x,int fa,int dep)
{
d[x]=dep;
f[x][0]=fa;
vis[x]=1;
for(int i=v2[x];i;i=p[i].ne)
{
inty=p[i].y;
if(y!=fa)
{
minp[y][0]=p[i].l;
dfs(y,x,dep+1);
}
}
}
void kru()
{
inttotal=0;
sort(e+1,e+num+1);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=num;i++)
{
intf1=get(e[i].x),f2=get(e[i].y);
if(f1!=f2)
{
fa[f1]=f2;
put2(e[i].x,e[i].y,e[i].l);
put2(e[i].y,e[i].x,e[i].l);
total++;
if(total==n-1) break;
}
}
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
vis[i]=1;
dfs(i,0,1);
}
}
}
int log_(int n)
{
intx=1,cnt=0;
while(x<n)
{
x*=2;
cnt++;
}
returncnt;
}
void pre()
{
for(int j=1;j<=15;j++)
for(int i=1;i<=n;i++)
{
f[i][j]=f[f[i][j-1]][j-1];
minp[i][j]=min(minp[i][j-1],minp[f[i][j-1]][j-1]);
}
}
int LCA(int x,int y)
{
intmi=2100000000;
intd1=d[x],d2=d[y];
if(d1>d2) swap(x,y);
intdp=d[y]-d[x];
if(dp>0)
{
for(int i=0;d&&i<=14;i++)
{
if(dp&1)
{
mi=min(mi,minp[y][i]);
y=f[y][i];
}
dp>>=1;
}
}
if(x==y) return mi;
boolff=1;
intk=log_(d[x]);
while(ff)
{
if(f[x][0]==f[y][0]) break;
for(int i=k;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
mi=min(mi,minp[x][i]);
mi=min(mi,minp[y][i]);
x=f[x][i];
y=f[y][i];
k=i;
break;
}
}
}
mi=min(mi,minp[x][0]);
mi=min(mi,minp[y][0]);
return(mi);
}
int main()
{
ios::sync_with_stdio(false);
scanf("%d%d",&n,&m);
intx,y,z;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
put(x,y,z);
}
kru();
pre();
intp,root;
scanf("%d",&p);
for(int i=1;i<=p;i++)
{
scanf("%d%d",&x,&y);
intk1=get(x),k2=get(y);
if(k1!=k2)
{
printf("%d\n",-1);
continue;
}
ans=LCA(x,y);
printf("%d\n",ans);
}
}