题目链接: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 }
运用前序后序的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 }
然后问题就来了,这个在vijos上AC的代码在meto code上还是WA了,我已经放弃了,因为meto code不提供错误数据也不告诉我错了几组,所以我最终还是没有在meto code上成功