[vijos1460&Metocode P223]拉力赛<LCA>

题目链接:https://vijos.org/p/1460

    http://oj.fjaxyz.com:3389/problem.php?id=223

我不禁开始怀疑,这,真的是最近公共祖先的题吗,我是从一个讲解LCA的博客里找到这道题的,然后我就乖乖的用了可爱的LCA倍增,然后我就更加愉快的TLE了。

【思路】

判断u,v 的最近公共祖先是不是u,如果是,就加上这条路线。。。

判断u,v的最近公共祖先是不是u,TLE的代码是用倍增算法做的这个,但是啊,其实我们换一种说法就是判断u是不是v的祖先

那么这就简单了,想想LCA的离线做法tarjan,里面的dfn数组,储存的是i点的dfs序号,然后实质上其实是前序遍历序号

怎么判断u是v的祖先?刚刚才提到前序遍历,那我想想后序遍历,前序是根左右,后序是左右根,那么意思就是根在前序是比叶节点序号小,在后序是比叶节点序号大

于是我们储存数组begn[] ed[]储存点的前序序号和后序序号,如果begn[u]<begn[v]&&ed[u]>ed[v]说明u是v的祖先

解决了这个问题,接下来就是求距离,我们储存数组dis表示点i到根节点的距离,

所以u,v距离就是dis[u]+dis[v]-2*dis[lca(u,v)],由于这题满足条件是u为v的祖先,所以距离简化为dis[v]-dis[u]

LCA倍增算法

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cmath>
 6 #include<stack>
 7 #include<queue>
 8 #include<cstdlib> 
 9 #define maxn 10005
10 #define ll long long
11 using namespace std;
12 
13 struct node{
14     int u,v;ll w;int nxt;
15 }e[maxn];
16 
17 int head[maxn],n,m,f[maxn][20];
18 ll dis[maxn],ans;
19 int cnt,vis[maxn],dep[maxn];
20 
21 int tot;
22 void adde(int u,int v,int w){
23     e[tot]=(node){u,v,w,head[u]};
24     head[u]=tot;tot++;
25 }
26 
27 void build(int u){    
28     for(int i=head[u];i!=-1;i=e[i].nxt){
29         int v=e[i].v;
30         if(!vis[v]){
31             f[v][0]=u;
32             dis[v]=dis[u]+e[i].w;
33             dep[v]=dep[u]+1;
34             vis[v]=1;
35             build(v);
36         }
37     }
38 }
39 
40 void first(){
41     for(int j=1;j<=16;j++){
42         for(int i=1;i<=n;i++){
43             f[i][j]=f[f[i][j-1]][j-1];
44         }
45     }
46 }
47 
48 int up(int x,int d){
49     for(int i=1;i<=d;i++)
50         x=f[x][0];
51     return x;
52 }
53 
54 int find(int x,int y){
55     if(dep[x]<dep[y])swap(x,y);
56     if(dep[x]!=dep[y]){
57         int dd=dep[x]-dep[y];
58         x=up(x,dd);
59     }
60     if(x==y){return x;}
61     for(int j=16;j>=0;j--){
62         if(f[x][j]!=f[y][j]){
63             x=f[x][j];y=f[y][j];
64         }
65     }
66     return f[x][0];
67 }
68 
69 ll len(int u,int v){
70     return dis[v]-dis[u];
71 }
72 
73 int main(){
74     memset(head,-1,sizeof(head));
75     scanf("%d%d",&n,&m);
76     for(int i=1;i<n;i++){
77         int u,v,w;
78         scanf("%d%d%d",&u,&v,&w);
79         adde(u,v,w);
80     }
81     build(1);
82     first();
83     for(int i=1;i<=m;i++){
84         int u,v;
85         scanf("%d%d",&u,&v);
86         int lca=find(u,v);
87         if(lca==u){ans+=len(u,v);cnt++;}
88     }
89     printf("%d
%lld",cnt,ans);
90 }
倍增(TLE)

运用前序后序的dfs算法

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cmath>
 6 #include<stack>
 7 #include<queue>
 8 #include<cstdlib> 
 9 #define maxn 10005
10 using namespace std;
11 
12 struct node{
13     int u,v;long long w;int nxt;
14 }e[maxn];
15 
16 int head[maxn],n,m;
17 int cnt,vis[maxn];
18 int begn[maxn],ed[maxn];
19 long long ans,dis[maxn];
20 
21 int tot;
22 void adde(int u,int v,long long w){
23     e[tot]=(node){u,v,w,head[u]};
24     head[u]=tot;tot++;
25 }
26 
27 int num,hehe;
28 void build(int u){    
29     num++;begn[u]=num;
30     for(int i=head[u];i!=-1;i=e[i].nxt){
31         int v=e[i].v;
32         if(!vis[v]){;
33             dis[v]=dis[u]+e[i].w;
34             vis[v]=1;
35             build(v);
36         }
37     }
38     hehe++;ed[u]=hehe;
39 }
40 
41 int main(){
42     memset(head,-1,sizeof(head));
43     scanf("%d%d",&n,&m);
44     for(int i=1;i<n;i++){
45         int u,v;long long w;
46         scanf("%d%d%lld",&u,&v,&w);
47         adde(u,v,w);
48     }
49     build(1);
50     for(int i=1;i<=m;i++){
51         int u,v;
52         scanf("%d%d",&u,&v);
53         if(begn[u]<=begn[v]&&ed[u]>=ed[v]){
54             ans+=dis[v]-dis[u];cnt++;
55         }
56     }
57     printf("%d
%lld",cnt,ans);
58 }
AC于vijos

然后问题就来了,这个在vijos上AC的代码在meto code上还是WA了,我已经放弃了,因为meto code不提供错误数据也不告诉我错了几组,所以我最终还是没有在meto code上成功

原文地址:https://www.cnblogs.com/Danzel-Aria233/p/7777063.html