[codevs2370]小机房的树<LCA>

题目链接:http://codevs.cn/problem/2370/

这题我还是做了比较久了,因为有人告诉我这是用tarjan离线做

好吧算我是蒟蒻,真心不懂tarjan怎么做,最后还是用倍增做的

 

所以我也就借着这题复习了一下RMQ了。。

思想就是定义两个数组f[i][j],dis[i][j]分别表示从i上升2^j层到达的节点和需要的价值

然后就是让两个询问节点先在统一深度需要的价值加上到达同一节点需要的价值,没啥难度,看看难度就懂了

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cmath>
 6 #include<cstdlib>
 7 #include<queue>
 8 #define maxn 50005
 9 using namespace std;
10 //倍增
11 int n,m,dis[maxn][30],f[maxn][30],dep[maxn],head[maxn];
12 struct edge{
13     int u,v,w,nxt;
14 }e[maxn*10]; 
15 
16 int tot;
17 void adde(int u,int v,int w){
18     e[++tot].u=u;
19     e[tot].v=v;
20     e[tot].w=w;
21     e[tot].nxt=head[u];
22     head[u]=tot;
23 }
24 
25 void build(int u)
26 {
27     for(int i=head[u];i!=-1;i=e[i].nxt){
28         int v=e[i].v,w=e[i].w;
29         if(f[v][0]==0){
30             f[v][0]=u;
31             dep[v]=dep[u]+1;
32             dis[v][0]=w;
33             build(v);
34         }
35     }
36 }
37 
38 void first(){
39     for(int j=1;j<=20;j++){
40         for(int i=1;i<=n;i++){
41             f[i][j]=f[f[i][j-1]][j-1];
42             dis[i][j]=dis[i][j-1]+dis[f[i][j-1]][j-1];
43         }
44     }
45 }
46 
47 int sum=0;
48 int lca(int a,int b){
49     if(dep[a]<dep[b])swap(a,b);//保证a的深度要深一些
50     if(dep[a]!=dep[b]){//让不同深度到相同深度 
51         int d=dep[a]-dep[b];
52         for(int i=0;d;i++){
53             if(d&1){
54                 sum+=dis[a][i];
55                 a=f[a][i];
56             }
57             d=d>>1;//这里是经典的类似二进制的方法分解成2^i1+2^i2+…… 
58         }    
59     }
60     if(a==b)return sum;
61     for(int i=20;i>=0;i--){
62         if(f[a][i]!=f[b][i]){//两个人刚刚好达不到根节点
63         //这样的意义就是不断的缩小向上扩展的深度,可以到达终点
64         //因为是不等于才执行,所以循环完了肯定还是到不了
65             sum+=dis[a][i]+dis[b][i];
66             a=f[a][i];b=f[b][i]; 
67         }
68     }
69     sum+=dis[a][0]+dis[b][0];//2^i=2^i-1+2^i-2+2^1+2^0+2^0
70     return sum;
71      
72 }
73 
74 int main(){
75     memset(head,-1,sizeof(head));
76     scanf("%d",&n);
77     for(int i=1;i<n;i++)
78     {
79         int u,v,w;
80         scanf("%d%d%d",&u,&v,&w);
81         adde(u+1,v+1,w);adde(v+1,u+1,w);
82     }
83     f[1][0]=1;//倍增过界了就默认为到了根节点 
84     build(1);
85     first();
86     scanf("%d",&m);
87     for(int i=1;i<=m;i++)
88     {
89         int a,b;sum=0;
90         scanf("%d%d",&a,&b);
91         int ans=lca(a+1,b+1);
92         printf("%d\n",ans);
93     }
94 }
View Code

 

转载于:https://www.cnblogs.com/Danzel-Aria233/p/7669971.html

下面的代码是干什么用的,请生成说明注释,同时还有什么改进: #include <iostream> #include <vector> #include <cmath> #include <algorithm> using namespace std; const int MAXN = 100010; const int MAXLOG = 20; vector<int> tree[MAXN]; int depth[MAXN], parent[MAXN][MAXLOG]; void dfs(int u, int p) { depth[u] = depth[p] + 1; parent[u][0] = p; for (int j = 1; j < MAXLOG; j++) { parent[u][j] = (parent[u][j-1] == -1) ? -1 : parent[parent[u][j-1]][j-1]; } for (int v : tree[u]) { if (v != p) dfs(v, u); } } void preprocess(int root, int n) { fill(depth, depth + n + 1, 0); for (int i = 0; i <= n; i++) { for (int j = 0; j < MAXLOG; j++) parent[i][j] = -1; } depth[root] = 0; for (int v : tree[root]) dfs(v, root); } int lca(int a, int b) { if (depth[a] < depth[b]) swap(a, b); int j = log2(depth[a]) + 1; for (int k = j; k >= 0; k--) { if (depth[a] - (1 << k) >= depth[b]) a = parent[a][k]; } if (a == b) return a; for (int k = j; k >= 0; k--) { if (parent[a][k] != -1 && parent[a][k] != parent[b][k]) { a = parent[a][k]; b = parent[b][k]; } } return parent[a][0]; } int main() { int n = 5; // 节点数 // 示例: 1-2, 1-3, 2-4, 2-5 tree[1].push_back(2); tree[2].push_back(1); tree[1].push_back(3); tree[3].push_back(1); tree[2].push_back(4); tree[4].push_back(2); tree[2].push_back(5); tree[5].push_back(2); preprocess(1, n); // 根节点为1 cout << "LCA(4,5): " << lca(4,5) << endl; // 输出2 cout << "LCA(3,4): " << lca(3,4) << endl; // 输出1 return 0; } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return new BigInteger(1, digest).toString(16); }】
07-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值