[关闭]
@WangYixu 2018-06-15T11:25:07.000000Z 字数 2212 阅读 779

[NOIP2013]货车运输

OI 题解 LCA MST

算法:LCA, MST

首先建立最大生成树,因为两个节点互相到达必须要经过最大生成树(反证法)。
然后跑LCA+ST就可以了。

代码详解

数据结构:

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<cctype>
  4. #include<algorithm>
  5. #define il inline
  6. using std::sort;
  7. using std::min;
  8. using std::swap;
  9. const int N = 10000 + 10;
  10. const int M = 2 * 50000 + 10;
  11. const int Q = 2 * 30000 + 10;
  12. const int P = 25;
  13. const int INF = 0x3f3f3f3f;
  14. int n, m, qs;
  15. int head[N], to[M], next[M], v[M], cnt;
  16. int fa[N], rt[N][P], dep[N], st[N][P];
  17. struct edge{
  18. int from, to, v;
  19. friend bool operator < (edge a, edge b) {
  20. return a.v > b.v;
  21. }
  22. } e[M];

快读:

这里有两种写法
一种是 ans = (ans<<3)+(ans<<1)+(c^`0`), 另一种是ans = ans*10+c-'0', 前者更快(约140ms)。

  1. il int read() {
  2. char c;
  3. while(!isdigit(c = getchar()));
  4. int ans = c ^ '0';//mark: '0' = 0x30;
  5. while(isdigit(c = getchar())) {
  6. ans = (ans << 3) + (ans << 1) + (c ^ '0');//mark:二进制要加括号
  7. //ans = ans * 10 + c - '0';
  8. }
  9. return ans;
  10. }

并查集:

  1. il void init() {
  2. for(register int i = 1; i <= n; ++i)
  3. fa[i] = i;
  4. }
  5. int find(const int &x) {
  6. if(fa[x] == x) {
  7. return fa[x];
  8. }
  9. fa[x] = find(fa[x]);
  10. return fa[x];
  11. }

MST:

  1. il void adde(const int &x, const int &y,const int &z) {
  2. cnt++;
  3. to[cnt] = y;
  4. v[cnt] = z;
  5. next[cnt] = head[x];
  6. head[x] = cnt;
  7. }
  8. il void kruskal() {
  9. sort(e + 1, e + m + 1);
  10. init();
  11. for(register int i = 1, fx, fy; i <= m; ++i) {
  12. fx = find(e[i].from);
  13. fy = find(e[i].to);
  14. if(fx != fy) {
  15. adde(e[i].from, e[i].to, e[i].v);
  16. adde(e[i].to, e[i].from, e[i].v);
  17. fa[fx] = fy;
  18. }
  19. }
  20. }

无根树转有根树:

顺便st表初始化。

  1. void dfs(int pos, int f, int vi) {
  2. // printf("%d %d %d\n", pos, f, vi);
  3. rt[pos][0] = f;
  4. dep[pos] = dep[f] + 1;
  5. st[pos][0] = vi;
  6. for(int i = 1; (1 << i) < dep[pos]; ++i) {
  7. // printf("%d %d\n", pos, i);
  8. rt[pos][i] = rt[rt[pos][i-1]][i-1];
  9. st[pos][i] = min(st[rt[pos][i-1]][i-1], st[pos][i-1]);
  10. }
  11. for(register int i = head[pos]; i; i = next[i]) {
  12. if(to[i] != f) {
  13. dfs(to[i], pos, v[i]);
  14. }
  15. }
  16. }

LCA:

倍增算法

  1. il int lca(int x, int y) {
  2. int ans = INF;
  3. if(dep[x] > dep[y])
  4. swap(x, y);
  5. for(int i = 20; i >= 0; --i) {
  6. if(dep[y] - (1<<i) >= dep[x]) {
  7. ans = min(ans, st[y][i]);
  8. y = rt[y][i];
  9. }
  10. }
  11. if(x == y) return ans;
  12. for(int i = 20; i >= 0; --i) {
  13. if(rt[x][i] != rt[y][i]) {
  14. ans = min( ans, min(st[x][i], st[y][i]) );
  15. x = rt[x][i];
  16. y = rt[y][i];
  17. }
  18. }
  19. ans = min( ans, min(st[x][0], st[y][0]) );
  20. return ans;
  21. }

To be continued:Tarjan

主函数:

  1. int main() {
  2. n = read();
  3. m = read();
  4. for(register int i = 1, x, y, z; i <= m; ++i) {
  5. e[i].from = read();
  6. e[i].to = read();
  7. e[i].v = read();
  8. }
  9. memset(st, 0x3f, sizeof st);
  10. kruskal();
  11. for(int i = 1; i <= n; ++i) {
  12. if(!rt[i][0])
  13. dfs(i, i, 0);
  14. }
  15. qs = read();
  16. for(register int i = 1, x, y; i <= qs; ++i) {
  17. x = read(); y = read();
  18. if(find(x) != find(y)) {
  19. printf("-1\n");
  20. continue;
  21. }
  22. printf("%d\n", lca(x, y));
  23. }
  24. }

后话

这个题应该还可以用Tarjan实现,未完待续。

听说可以不跑LCA,用SPFA,未完待续。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注