A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。
4 3 1 2 4 2 3 3 3 1 1 3 1 3 1 4 1 3
3 -1 3
0 < n < 10,000,0 < m < 50,000,0 < q < 30,000,0 ≤ z ≤ 100,000。
刚拿到题的时候确实很纠结,暴力的方法比较好想,就是从起点跑一次类似SPFA的算法,只不过求的值是两点间最小边的最大值。这个想法启示我们,会影响答案的是尽可能大的边,也就是说,如果有两条边在图中连接着同样的块,那么显然只需要考虑较大边就行了。再推而广之,在保证不破坏原图连通性的情况下,我们可以保存尽可能大的边,其它的边都可以尽可能的删去。
这样一来,问题就显然了:求最大生成树中两点之间路径的边中的最小值。对于求树中两点间的路径信息,可以考虑求最近公共祖先(LCA),求LCA的比较通用的在线算法是树上路径倍增,对于这道题,倍增数组中记录一个点的第2^k个祖先,和他到第2^k个祖先这段唯一路径中的边的最小值,然后先讲矮的那个点提到和另一个点同样的高度,让后两边一起爬树即可。
但是这道题N很小,只有10000,树的深度就更小了,并不需要对数级的算法。于是我就YY了个树上路径分块,就是把每一条从根到叶子的路径分成根号h个块,每个块占根号h层。和路径倍增法类似,先调整到同一深度,然后一起爬树。具体实现用两个数组,一个表示该叶子节点所属的块的顶部的节点,一个表示它到这个节点的路径中的最小边。在相距较远的时候先一个块一个块地比较猛地往上爬(虽然不如倍增爬得猛),然后再一个一个地往上爬,爬的时候更新答案即可,求一次最近公共祖先复杂度为根号h。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) const int MAXN = 10010, MAXM = 50010; const int INF = 1<<27; int N, M, Q; struct Node { int to, z; Node*next; } Edge1[MAXM*2], *ecnt1=Edge1, *adj1[MAXN]; void add1(int a, int b, int z) //原图中的连边 { ++ecnt1; ecnt1->to = b; ecnt1->z = z; ecnt1->next = adj1[a]; adj1[a] = ecnt1; } int belong[MAXN]; struct Road {//用于kruskal的边数组 int a, b, z; Road(){} Road(int i,int j,int k) { a=i; b=j; z=k; } bool operator < (const Road&t) const { return z > t.z; } } rd[MAXM*2]; int tp, cnt; //临时记录当前块的边的数量和点的数量 bool vis[MAXN]; void dfs1(int u, int&rt) { vis[u] = 1; belong[u] = rt; ++cnt; for (Node*p = adj1[u]; p; p=p->next) { rd[++tp] = Road(u, p->to, p->z);//连通块涉及的边用于kruskal if (!vis[p->to]) dfs1(p->to, rt); } } Node Edge[MAXM*2], *ecnt=Edge, *adj[MAXN]; //存最大生成树 void add(int a, int b, int z) { ++ecnt; ecnt->to = b; ecnt->z = z; ecnt->next = adj[a]; adj[a] = ecnt; } struct bcset { //bing cha set int tfa[MAXN]; bcset() { for (int i = 1; i<MAXN; ++i) tfa[i] = i; } int root(int a) { if (tfa[a]==a) return a; return tfa[a] = root(tfa[a]); } void unite(int a, int b) { tfa[root(a)]= root(b); } } klu; void kruskal() //最大生成树 { int i, c = 0, a, b; sort(rd+1, rd+tp+1); for (i = 1; i<=tp; ++i) { a = rd[i].a; b = rd[i].b; if (klu.root(a) != klu.root(b)) { klu.unite(a, b); ++c; add(a, b, rd[i].z); add(b, a, rd[i].z); } if (c==cnt-1) break; } } int mxdep, bdep;//最大深度和分块的深度 int fa[MAXN], dep[MAXN]; int falmt[MAXN]; //这个点到父亲那条边的限制 void dfstree(int u) { if (dep[u] > mxdep) mxdep = dep[u]; for (Node*p = adj[u]; p; p=p->next) if (p->to != fa[u]) { fa[p->to] = u; dep[p->to] = dep[u]+1; falmt[p->to] = p->z; dfstree(p->to); } } int blocktop[MAXN], blockmin[MAXN];//这个点所在块的顶端,到顶端的边中的最小值 void divide(int u) //路径分块 { if (dep[u] % bdep == 0) //新分出一块 blocktop[u] = u, blockmin[u] = INF; else { blocktop[u] = blocktop[fa[u]]; blockmin[u] = Min(blockmin[fa[u]], falmt[u]); } for (Node*p = adj[u]; p; p=p->next) if (p->to != fa[u]) divide(p->to); } int query(int a, int b)//a,b到最近公共祖先的路径中的最小边 { if (belong[a] != belong[b]) return -1; int res = INF; if (dep[a]<dep[b]) { int c=a; a=b; b=c; } //以下两个循环将a,b调到一个高度 while (dep[blocktop[a]] > dep[b]) { res = Min(res, Min(falmt[blocktop[a]], blockmin[a])); a = fa[blocktop[a]]; } while (dep[a] > dep[b]) res = Min(res, falmt[a]), a = fa[a]; //以下两个循环求最近公共祖先 while (blocktop[a] != blocktop[b]) { res = Min(res, Min(blockmin[a], blockmin[b])); a = blocktop[a], b = blocktop[b]; res = Min(res, Min(falmt[a], falmt[b])); a = fa[a], b = fa[b]; } while (a != b) { res = Min(res, Min(falmt[a], falmt[b])); a = fa[a], b = fa[b]; } return res; } void BuildTree() { for (int i = 1; i<=N; ++i) { if (vis[i]) continue; tp = cnt = mxdep = 0; dfs1(i, i); kruskal(); dfstree(i); bdep = sqrt(mxdep+1.5); //防止bdep为0 divide(i); } } int main() { int i, a, b, c; scanf("%d%d", &N, &M); for (i = 1; i<=M; ++i) { scanf("%d%d%d", &a, &b, &c); if (c==0) continue; add1(a,b,c); add1(b,a,c); } BuildTree(); scanf("%d", &Q); while (Q--) { scanf("%d%d", &a, &b); printf("%d\n", query(a, b)); } return 0; }