zoukankan      html  css  js  c++  java
  • hdu2874(lca / tarjan离线 + RMQ在线)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2874

    题意: 给出 n 个顶点 m 条边的一个森林, 有 k 个形如 x y 的询问, 输出 x, y 之间的最短路径.

    思路: 如果将森林换成一棵树的话就是一道 lca 模板题了, 不过本题需要稍作改动.

    解法1: tarjan

    只需要先判断一下 x, y 是否在一颗树里面就 ok 了, 不过这道题的询问有点多, 很容易 mle.

    代码:

      1 #include <iostream>
      2 #include <stdio.h>
      3 #include <string.h>
      4 using namespace std;
      5 
      6 const int MAXN = 1e4 + 10;
      7 const int MAX = 1e6 + 10;
      8 struct node1{
      9     int v, w, next;
     10 }edge1[MAXN << 1];
     11 
     12 struct node2{
     13     int u, v, next;
     14 }edge2[MAX << 1];//edge1记录树, edge2记录询问
     15 
     16 int vis[MAXN], pre[MAXN], dis[MAXN], sol[MAX];//vis[i]标记i是否已经搜索过, pre[i]记录i的根节点, dis[i]记录i到根节点的距离
     17 int head1[MAXN], head2[MAXN], ip1, ip2, cnt;
     18 
     19 void init(void){
     20     memset(sol, 0, sizeof(sol));
     21     memset(vis, 0, sizeof(vis));
     22     memset(dis, 0, sizeof(dis));
     23     memset(head1, -1, sizeof(head1));
     24     memset(head2, -1, sizeof(head2));
     25     ip1 = ip2 = cnt = 0;
     26 }
     27 
     28 void addedge1(int u, int v, int w){//前向星
     29     edge1[ip1].v = v;
     30     edge1[ip1].w = w;
     31     edge1[ip1].next = head1[u];
     32     head1[u] = ip1++;
     33 }
     34 
     35 void addedge2(int u, int v){
     36     edge2[ip2].u = u;
     37     edge2[ip2].v = v;
     38     edge2[ip2].next = head2[u];
     39     head2[u] = ip2++;
     40 }
     41 
     42 int find(int x){
     43     return pre[x] == x ? x : pre[x] = find(pre[x]);
     44 }
     45 
     46 void jion(int x, int y){
     47     x = find(x);
     48     y = find(y);
     49     if(x != y) pre[y] = x;
     50 }
     51 
     52 void tarjan(int u){
     53     pre[u] = u;
     54     vis[u] = cnt;//将不同的联通块标记成不同的数字
     55     for(int i = head1[u]; i != -1; i = edge1[i].next){
     56         int v = edge1[i].v;
     57         int w = edge1[i].w;
     58         if(!vis[v]){
     59             dis[v] = dis[u] + w;
     60             tarjan(v);
     61             jion(u, v);
     62         }
     63     }
     64     for(int i = head2[u]; i != -1; i = edge2[i].next){
     65         int v = edge2[i].v;
     66         if(vis[v] == cnt) sol[i >> 1] = find(v);//只有同一个联通块的顶点才存在lca
     67     }
     68 }
     69 
     70 int main(void){
     71     int t, n, m, x, y, z;
     72     while(~scanf("%d%d%d", &n, &m, &t)){
     73         init();
     74         for(int i = 0; i < m; i++){
     75             scanf("%d%d%d", &x, &y, &z);
     76             addedge1(x, y, z);
     77             addedge1(y, x, z);
     78         }
     79         for(int i = 0; i < t; i++){
     80             scanf("%d%d", &x, &y);
     81             addedge2(x, y);
     82             addedge2(y, x);
     83         }
     84         for(int i = 1; i <= n; i++){
     85             if(!vis[i]){
     86                 cnt++;
     87                 dis[i] = 0;//顶点到自己的距离为0
     88                 tarjan(i);
     89             }
     90         }
     91         for(int i = 0; i < t; i++){
     92             int cc = i << 1;
     93             int u = edge2[cc].u;
     94             int v = edge2[cc].v;
     95             int lca = sol[i];//sol开两倍空间会超内存
     96             if(!lca) puts("Not connected");
     97             else printf("%d
    ", dis[u] + dis[v] - 2 * dis[lca]);
     98         }
     99     }
    100     return 0;
    101 }
    View Code

    解法2: lca 转 RMQ

    可以先拟个虚根, 另外输出前先判一下 x, y 是否在同一棵树里面即可.

    代码:

      1 #include <iostream>
      2 #include <stdio.h>
      3 #include <string.h>
      4 #include <math.h>
      5 using namespace std;
      6 
      7 const int inf = 1e9;
      8 const int MAXN = 4e4 + 10;
      9 struct node{
     10     int v, w, next;
     11 }edge[MAXN << 1];
     12 
     13 int dp[MAXN << 1][30]; //dp[i][j]存储deep数组中下标i开始长度为2^j的子串中最小值的下标
     14 int first[MAXN], ver[MAXN << 1], deep[MAXN << 1];
     15 int vis[MAXN], head[MAXN], dis[MAXN], tag[MAXN], ip, indx, cnt;
     16 
     17 inline void init(void){
     18     memset(tag, 0, sizeof(tag));
     19     memset(vis, 0, sizeof(vis));
     20     memset(head, -1, sizeof(head));
     21     ip = 0;
     22     indx = 0;
     23     cnt = 0;
     24 }
     25 
     26 void addedge(int u, int v, int w){
     27     edge[ip].v = v;
     28     edge[ip].w = w;
     29     edge[ip].next = head[u];
     30     head[u] = ip++;
     31 }
     32 
     33 void dfs(int u, int h){
     34     vis[u] = 1; //标记已搜索过的点
     35     ver[++indx] = u; //记录dfs路径
     36     first[u] = indx; //记录顶点u第一次出现时对应的ver数组的下标
     37     deep[indx] = h; //记录ver数组中对应下标的点的深度
     38     for(int i = head[u]; i != -1; i = edge[i].next){
     39         int v = edge[i].v;
     40         if(!vis[v]){
     41             dis[v] = dis[u] + edge[i].w;
     42             dfs(v, h + 1);
     43             ver[++indx] = u;
     44             deep[indx] = h;
     45         }
     46     }
     47 }
     48 
     49 void dfs1(int u){
     50     tag[u] = cnt;
     51     for(int i = head[u]; i != -1; i = edge[i].next){
     52         if(!tag[edge[i].v]) dfs1(edge[i].v);
     53     }
     54 }
     55 
     56 void ST(int n){
     57     for(int i = 1; i <= n; i++){
     58         dp[i][0] = i;
     59     }
     60     for(int j = 1; (1 << j) <= n; j++){
     61         for(int i = 1; i + (1 << j) - 1 <= n; i++){
     62             int x = dp[i][j - 1], y = dp[i + (1 << (j - 1))][j - 1];
     63             dp[i][j] = deep[x] < deep[y] ? x : y;
     64         }
     65     }
     66 }
     67 
     68 int RMQ(int l, int r){
     69     int len = log2(r - l + 1);
     70     int x = dp[l][len], y = dp[r - (1 << len) + 1][len];
     71     return deep[x] < deep[y] ? x : y;
     72 }
     73 
     74 int LCA(int x, int y){
     75     int l = first[x], r = first[y];
     76     if(l > r) swap(l, r);
     77     int pos = RMQ(l, r);
     78     return ver[pos];
     79 }
     80 
     81 int main(void){
     82     int n, m, k, x, y, z;
     83     while(~scanf("%d%d%d", &n, &m, &k)){
     84         init();
     85         for(int i = 0; i < m; i++){
     86             scanf("%d%d%d", &x, &y, &z);
     87             addedge(x, y, z);
     88             addedge(y, x, z);
     89         }
     90         for(int i = 1; i <= n; i++){
     91             if(!tag[i]){
     92                 cnt++;
     93                 dfs1(i);
     94                 addedge(i, 0, inf); //拟个虚根0
     95                 addedge(0, i, inf);
     96             }
     97         }
     98         dis[0] = 0;
     99         dfs(0, 1);
    100         ST(2 * n - 1);
    101         for(int i = 0; i < k; i++){
    102             scanf("%d%d", &x, &y);
    103             int lca = LCA(x, y);
    104             if(tag[x] != tag[y]) puts("Not connected");
    105             else printf("%d
    ", dis[x] + dis[y] - 2 * dis[lca]);
    106         }
    107     }
    108     return 0;
    109 }
    View Code
  • 相关阅读:
    having——至少被订购过两回的订单
    产品——仓库表查询
    SQL 聚集函数使用
    select count(*)和select count(1)的区别 (转)
    SpringAOP 通知(advice)
    Spring AOP 实现原理与 CGLIB 应用
    cglib 动态代理基础篇
    模仿Struts2的Interceptor拦截器实现
    利用JDK动态代理机制实现简单拦截器
    java多线程总结二:后台线程(守护线程)
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/7207651.html
Copyright © 2011-2022 走看看