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

题意:模拟DOS下的cd命令,给出n个节点的目录树以及m次查询,每个查询包含一个当前目录cur和一个目标目录tar,返回从cur切换到tar所要使用的cd命令次数:

注意这里的cd命令是简化版,只能进行如下两种操作:

  1. cd   ..                                        //返回父目录

  2. cd   cur\一系列目录\tar                 //由当前目录跳转到目标目录,注意中间的“一系列目录”不能包含父目录..,也就是说,自底向上必须一步步走,而自顶向下可以一步到位

思路:用Tarjan算法求LCA,处理查询时要分类讨论:

  1. if tar == lca,则res(cur, tar) = depth(cur) - depth(lca); (包含tar == cur的情况)

  2. else if cur == lca, 则res(cur, tar) = 1;

  3. 其他,则res(cur, tar) = depth(cur) - depth(lca) + 1;

这道题还有些细节问题需要处理好:

1. 每个查询要记录好目标目录是谁。因为tarjan算法是批处理的,即每完成对一个棵子树的遍历,处理其树根所涉及到的查询。为使查询处理不遗漏,我们把查询也以邻接表的形式存成双向边,然而每次处理需要知道本次查询原始的“单向边”,这样才能根据上面的分类计算结果。所以我另设了一个数组ans_tar[i]记录第 i 个查询所给定的tar。

2. 同HDU2586这道题,多个查询,注意记录查询序列号。这道题我用邻接表项query_id[r][i]记录节点r的第i个查询所持有的查询序列号,用邻接表项query_tar[r][i]记录节点r的第i个查询的目标节点。

3. 给出的目录是字符串,要用一个map<string, int>存储名称到节点号的映射关系。

 这里又了解到了map一个用法,即对[]的重载:对于map<string, int> m,如果调用一次m[s],而s在m中不存在时,会自动插入s并将它的value置为0。这个设计对于这道题很合适,可以维护全局计数变量seq_num,如果返回0的话,分发下一个序号给它即可。

  1 #include <cstdio>
  2 #include <iostream>
  3 #include <vector>
  4 #include <map>
  5 #include <string>
  6 #include <cstring>
  7 using namespace std;
  8 const int MAX_N = 100005;
  9 const int MAX_M = 100005;
 10
 11 int vis[MAX_N];
 12 int ans[MAX_N];
 13 int ans_tar[MAX_N];//每组查询的目标目录
 14 int indeg[MAX_N];
 15 int depth[MAX_N];
 16 map<string, int> name;
 17 vector<int> G[MAX_N];//邻接表,边不带权
 18 vector<int> query_tar[MAX_N];
 19 vector<int> query_id[MAX_N];
 20 int par[MAX_N];
 21
 22 int T;
 23 int n, m;
 24 int seq_num;//目录名的序列号,从1开始
 25
 26 void init(){
 27     seq_num = 1;
 28     memset(vis, 0, sizeof(vis));
 29     memset(ans, 0, sizeof(ans));
 30     memset(ans_tar, 0, sizeof(ans_tar));
 31     memset(indeg, 0, sizeof(indeg));
 32     memset(depth, 0, sizeof(depth));
 33     name.clear();
 34     for(int i=0; i<MAX_N; i++){
 35         G[i].clear();
 36         query_tar[i].clear();
 37         query_id[i].clear();
 38         par[i] = i;
 39     }
 40 }
 41 int find(int x){
 42     return par[x]==x ? x : par[x] = find(par[x]);
 43 }
 44 void unite(int x, int y){
 45     x = find(x);
 46     y = find(y);
 47     if(x==y) return ;
 48     par[y] = x;
 49 }
 50
 51 void dfs(int r, int l){
 52     //cout << "dfs " << r << endl;
 53     vis[r] = 1;
 54     depth[r] = l;
 55     for(int i=0; i<G[r].size(); i++){
 56         //cout << G[r][i] << endl;
 57         if(vis[G[r][i]]) continue;
 58         dfs(G[r][i], l+1);
 59         unite(r, G[r][i]);
 60     }
 61     for(int i=0; i<query_tar[r].size(); i++){
 62         if(!vis[query_tar[r][i]]) continue;
 63         int cur, tar;
 64         int ans_id = query_id[r][i];//这一查询所持的序列号
 65         int real_tar = ans_tar[ans_id];//这一个查询真正指定的target
 66         if(r == real_tar){//当前r是目标
 67             cur = query_tar[r][i];
 68             tar = real_tar;
 69         }else{//已访问过的那个点是目标
 70             cur = r;
 71             tar = real_tar;
 72         }
 73         //cout << "query " << cur << tar << endl;
 74         int ca = find(query_tar[r][i]);
 75         if(tar == ca) ans[ans_id] = depth[cur] - depth[ca];
 76         else if(cur == ca) ans[ans_id] = 1;
 77         else ans[ans_id] = depth[cur] - depth[ca] + 1;
 78     }
 79 }
 80
 81 void lca(int r){
 82     //cout << "root " << r << endl;
 83     dfs(r, 0);
 84 }
 85
 86 int main()
 87 {
 88     freopen("4547.txt", "r", stdin);
 89     scanf("%d", &T);
 90     while(T--){
 91         init();
 92         scanf("%d%d", &n, &m);
 93         for(int i=0; i<n-1; i++){
 94             string c, p;
 95             cin>>c;
 96             int u = name[c];//不存在会自动插入并置value为0
 97             if(u==0){
 98                 u = name[c] = seq_num++;
 99             }
100
101             cin>>p;
102             int v = name[p];
103             if(v==0){
104                 v = name[p] = seq_num++;
105             }
106             G[u].push_back(v);
107             G[v].push_back(u);
108             indeg[u]++;//入度为0的是根目录
109         }
110         // for(map<string, int>::iterator iter = name.begin();
111         //     iter != name.end(); iter++){
112         //     cout << iter->first << iter->second << endl;
113         // }
114         for(int i=0; i<m; i++){
115             string cur, tar;
116             cin >> cur >> tar;
117             int u = name[cur];
118             int v = name[tar];
119             query_tar[u].push_back(v);
120             query_tar[v].push_back(u);
121             query_id[u].push_back(i);//tar和id是同步记录的
122             query_id[v].push_back(i);
123             ans_tar[i] = v;//为辨谁是目标目录
124         }
125         for(map<string, int>::iterator iter = name.begin();
126             iter != name.end(); iter++){
127             int id = iter->second;
128             if(indeg[id] == 0){
129                 lca(id);
130                 break;
131             }
132         }
133         for(int i=0; i<m; i++){
134             printf("%d\n", ans[i]);
135         }
136     }
137     return 0;
138 }

【HDU 4547 CD操作】LCA问题 Tarjan算法相关推荐

  1. HDU - 4547 CD操作(LCA模板)

    题目链接:点击查看 题目大意:给出一个层层嵌套的树状结构,可以从某一点通过一个操作直接到达任意一个子节点的位置,但如果要从某个节点到达其祖先节点需要一层一层往上爬,问若要从节点A到达节点B,需要多少步 ...

  2. HDU 4547 CD操作

    传送门 没啥好说的.就是一个LCA. 不过就是有从根到子树里任意一个节点只需要一次操作,特判一下LCA是不是等于v.相等的话不用走.否则就是1次操作. 主要是想写一下倍增的板子. 倍增基于二进制.暴力 ...

  3. 【HDU - 1269】迷宫城堡 (tarjan算法模板)

    题干: 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只 ...

  4. 【HDU 1269】迷宫城堡 (Tarjan算法)

    迷宫城堡 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  5. Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)...

    转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2194090a96bbed2db1351de8.html 基本概念: 1.割点:若删掉某点后,原连通图 ...

  6. POJ 1470 Closest Common Ancestors (最近公共祖先LCA 的离线算法Tarjan)

    Tarjan算法的详细介绍,请戳: http://www.cnblogs.com/chenxiwenruo/p/3529533.html #include <iostream> #incl ...

  7. 0x66.图论 - Tarjan算法与无向图连通性

    目录 一.无向图的割点与桥 割点 桥/割边 时间戳 搜索树 追溯值 二.割边判定法则 三.割点判定法则 1.luogu P3388 [模板]割点(割顶) 2.luogu P3469 [POI2008] ...

  8. 『Tarjan算法 无向图的双联通分量』

    无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...

  9. tarjan算法与无向图的连通性(割点,桥,双连通分量,缩点)

    基本概念 给定无向连通图G = (V, E) 割点: 对于x∈V,从图中删去节点x以及所有与x关联的边之后,G分裂为两个或两个以上不相连的子图,则称x为割点 割边(桥) 若对于e∈E,从图中删去边e之 ...

最新文章

  1. 动态规划算法的优化技巧
  2. 计算机二级c在哪里学习,2017年计算机二级C语言考点学习
  3. pat天梯赛L1-055. 谁是赢家
  4. jz2440开发板移植U-boot之修改代码支持DM9000网卡
  5. 计算机选修课学什么,计算机专业都学什么 主要课程有什么
  6. springboot使用jsp完成数据的页面展示
  7. 【职业经验】测试转研发的一年总结
  8. react实现浏览器自动刷新_react调用什么刷新页面
  9. 类似QQ的可隐藏的便签工具SNOTE
  10. PAAS平台的理解及与LaaS,SaaS的关系
  11. 18V降压3.3V,15V降压3.3V的降压IC和LDO芯片方案
  12. struts2 xml 验证出现 Invalid field value for field 的解决方法(转)
  13. 5个好用的Excel技巧,一秒钟完成一个功能
  14. 入门学习编程培训有哪些科目课程适合?
  15. 微积分 = 微分 + 积分
  16. 网站SEO诊断分析要点
  17. 用python 实现发射爱心
  18. Python实现杨辉三角(2种实现方案)
  19. 一杯茶的功夫让你学会OA选型
  20. Avalonia 部署到麒麟信安操作系统

热门文章

  1. android studio查看字节码,使用Android studio查看Kotlin的字节码教程
  2. 基于直方图的图像增强算法(HE、CLAHE、Retinex)
  3. 全球及中国自动驾驶行业应用领域及投资前景展望报告2022-2028年版
  4. 中国氨纶市场“十四五”规划及未来动态分析报告2021年版
  5. 全球及中国抓紧器行业十四五发展态势及前景规划建议报告2021-2027年版
  6. Web开发兼容性系列文章(一):不同设备浏览器的userAgent值大全
  7. Codeforces Global Round 4 题解
  8. java:合并两个排序的整数数组A和B变成一个新的数组。新数组也要有序。
  9. 【原创】大数据基础之Ambari(1)简介、编译安装、使用
  10. MySQL高级知识(十五)——主从复制