noip2013 货车运输
题目描述
A国有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。
根据题目得知,我们可以不用考虑路径有多长,只用考虑是否能到以及经过路径的最大值。
这样我们可以把原图化简为一个最大生成树,保证原图能到达的点,化简后也能到达。
然后对于每一个x、y,只用求出它们的路径中间的最小值就可以了。用LCA就行
但是!我们发现,输入的图不一定是联通的!也就是,我们生成的不一定是最大生成树而是最大生成森林!
这样,我们跑LCA会原地爆炸!
故此,我们可以在构建完最大森林后,将所有森林的根节点连到一个虚拟节点0,。
如果LCA时,发现两个的LCA是0,那么就输出-1.
#include <iostream> #include <cstdio> #include <queue> #include <cstdlib> #include <cstring> #include <algorithm> #define REP(i,k,n) for(int i=k;i<=n;i++) #define in(a) a=read() #define MAXN 500010 using namespace std; inline int read(){ int x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } int n,m,p,ans; int f[MAXN]; queue <int> Q; int total=0,head[MAXN],to[MAXN<<1],nxt[MAXN<<1],val[MAXN<<1]; int vis[MAXN],depth[MAXN]; int tree[MAXN][30],minn[MAXN][30]; struct node{ int a,b,c; }l[MAXN]; inline bool cmp(node a,node b){ return a.c>b.c; } inline int getf(int k){//并查集求最大生成森林 if(f[k]==k) return k; return f[k]=getf(f[k]); } inline void adl(int a,int b,int c){ total++; to[total]=b; val[total]=c; nxt[total]=head[a]; head[a]=total; return ; } inline void bfs(){//LCA预处理 Q.push(0); vis[0]=1; while(!Q.empty()){ int u=Q.front(); Q.pop(); REP(i,1,18) tree[u][i]=tree[tree[u][i-1]][i-1],minn[u][i]=min(minn[u][i-1],minn[tree[u][i-1]][i-1]); for(int e=head[u];e;e=nxt[e]) if(!vis[to[e]]){ vis[to[e]]=1; depth[to[e]]=depth[u]+1; tree[to[e]][0]=u; minn[to[e]][0]=val[e]; Q.push(to[e]); } } return ; } inline int lca(int u,int v){//跑LCA if(depth[u]<depth[v]) swap(u,v); int d=depth[u]-depth[v]; REP(i,0,18) if(d&(1<<i)){ ans=min(ans,minn[u][i]); u=tree[u][i]; } if(u==v) return u; for(int i=18;i>=0;i--) if(tree[u][i]!=tree[v][i]){ ans=min(ans,min(minn[u][i],minn[v][i])); u=tree[u][i]; v=tree[v][i]; } ans=min(ans,min(minn[u][0],minn[v][0])); u=tree[u][0]; v=tree[v][0]; return u; } int main(){ in(n);in(m); REP(i,1,n) f[i]=i; REP(i,1,m) in(l[i].a),in(l[i].b),in(l[i].c); sort(l+1,l+m+1,cmp); REP(i,1,m){ int f1=getf(l[i].a),f2=getf(l[i].b); if(f1!=f2){ adl(l[i].a,l[i].b,l[i].c); adl(l[i].b,l[i].a,l[i].c); f[f2]=f1; } } REP(i,1,n)//构建虚拟节点 if(!vis[i]){ adl(0,i,0); bfs();//对于森林里的每一颗树都跑一边预处理 } in(p); int u,v; REP(i,1,p){ ans=2147483647; in(u),in(v); if(lca(u,v)) cout<<ans<<endl; else cout<<"-1"<<endl; } return 0; }