[codevs1036]商务旅行<LCA:tarjan&倍增>
题目链接:http://codevs.cn/problem/1036/
今天翻箱倒柜的把这题翻出来做了,以前做的时候没怎么理解,所以今天来重做一下
这题是一个LCA裸题,基本上就把另一道裸题小机房的树拿出来改一改就行
但LCA也有两种方式,倍增和tarjan,倍增我个人觉得很好理解,tarjan就有点迷了
所以我就用了两种方式打这一道题
倍增:
倍增的做法就是数组f[i][j]表示从i点往上走2^j次方个点可以到达哪个点,
然后进行一个树上倍增,记录下一个深度dep,然后让我们求的两点到同一深度,如果是同一点就return
不是同一点就倍增,倍增这个位置的操作和rmq差不多了,就是找到可以最大跳跃的深度,然后不断找。。直到差一个深度到最近公共祖先
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdlib>
#define maxn 30005
using namespace std; struct edge{
int u,v,nxt;
}e[maxn*]; int n,m,a[maxn],now[maxn],f[maxn][];
int head[maxn],dep[maxn],vis[maxn],ans; int tot;
void adde(int u,int v){
e[tot]=(edge){u,v,head[u]};
head[u]=tot++;
} void first(){
for(int j=;j<=;j++){
for(int i=;i<=n;i++){
f[i][j]=f[f[i][j-]][j-];
}
}
} void build(int u){
for(int i=head[u];i!=-;i=e[i].nxt){
int v=e[i].v;
if(!vis[v]){
f[v][]=u;
dep[v]=dep[u]+;
vis[v]=;
build(v);
}
}
} int up(int x,int d){
for(int i=;i<=d;i++)
x=f[x][];
return x;
} int find(int x,int y){
if(dep[x]<dep[y])swap(x,y);//手动使x深度深一些
if(dep[x]!=dep[y]){
int dd=dep[x]-dep[y];
x=up(x,dd);
}
if(x==y){return x;}
for(int j=;j>=;j--){
if(f[x][j]!=f[y][j]){
x=f[x][j];y=f[y][j];
}
}
return f[x][];
} int main(){
memset(head,-,sizeof(head));
memset(dep,,sizeof(dep));
scanf("%d",&n);
for(int i=;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
adde(x,y);adde(y,x);
}vis[]=;
build();
first();
scanf("%d",&m);
now[]=;
for(int i=;i<=m;i++){
scanf("%d",&now[i]);
int lca=find(now[i-],now[i]);
ans+=dep[now[i-]]+dep[now[i]]-*dep[lca];
}
printf("%d",ans);
}
倍增
tarjan:
虽然有些大佬觉得tarjan比倍增好理解,可能是由于我太弱导致我不能理解tarjan,说实话我觉得tarjan这个算法本身就很迷
tarjan的步骤:
1.判断与u相连的点,如果没来过就继续往深搜索下去
2.用并查集维护u,v的关系,讲两个点合并,将v标记来过
3.查找与u有询问关系的点,如果那个点已经被标记来过就继续
4.这时候找到的v的所属的并查集数组fa[v]储存的就是u,v的最近公共祖先
5.这时候u,v的距离就是u的深度+v的深度-两个最近公共祖先深度
dep[u]+dep[v]-2*dep[fa[v]];
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdlib>
#define maxn 30005
using namespace std; struct node{
int u,v,nxt,w;
}e[maxn*],q[maxn*]; int ans,n,m,head[maxn],heaq[maxn],dep[maxn];
int low[maxn],dfn[maxn],fa[maxn],num;
int now[maxn],cnt,vis[maxn],vise[maxn]; int tot;
void adde(int u,int v){
e[tot]=(node){u,v,head[u],};
head[u]=tot;tot++;
} int toq;
void addp(int u,int v,int w){
q[toq]=(node){u,v,heaq[u],w};
heaq[u]=toq;toq++;
} int find(int x){
if(x==fa[x])return x;
return fa[x]=find(fa[x]);
} void tarjan(int u){
num++;fa[u]=u;
dfn[u]=low[u]=num;vis[u]=;
for(int i=head[u];i!=-;i=e[i].nxt){
int v=e[i].v;
if(!dfn[v]){
dep[v]=dep[u]+;
tarjan(v);
fa[v]=u;
}
}
for(int i=heaq[u];i!=-;i=q[i].nxt ){
int v=q[i].v;
if(vis[v]&&!vise[q[i].w]){
vise[q[i].w]=;
ans+=dep[v]+dep[u]-*dep[find(v)];
}
} } int main(){
memset(head,-,sizeof(head));
memset(heaq,-,sizeof(heaq));
scanf("%d",&n);
for(int i=;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
adde(x,y);adde(y,x);
}
scanf("%d",&m);
for(int i=;i<=m;i++){
scanf("%d",&now[i]);
if(i==&&now[i]!=)addp(,now[i],i);
else {
addp(now[i-],now[i],i);addp(now[i],now[i-],i);
}
}dep[]=;
tarjan();
printf("%d",ans);
}
我一个同学教导我,如果你tarjan不理解就画图推,还是推不出来就背就行了,反正又不难背
[codevs1036]商务旅行<LCA:tarjan&倍增>的更多相关文章
- codevs1036商务旅行(LCA)
1036 商务旅行 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description 某首都城市的商人要经常到各城镇去做 ...
- [codevs1036] 商务旅行
题目描述 Description 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从首都出发,其他各城镇之间都有道路连接,任 ...
- 【codevs1036】商务旅行 LCA 倍增
1036 商务旅行 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 某首都城市的商人要经常到各城镇去做生意,他们按自己的 ...
- C++之路进阶——codevs1036(商务旅行)
1036 商务旅行 题目描述 Description 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从首都出发,其他各城镇 ...
- 倍增法-lca codevs 1036 商务旅行
codevs 1036 商务旅行 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 某首都城市的商人要经常到各城镇去做生意 ...
- 2953: [Poi2002]商务旅行
2953: [Poi2002]商务旅行 Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 8 Solved: 8[Submit][Status] Desc ...
- poj3728 商务旅行
[Description]小 T 要经常进行商务旅行,他所在的国家有 N 个城镇,标号为 1,2,3,...,N,这 N 个城镇构成一棵树.每个城镇可以买入和卖出货物,同一城镇买入和卖出的价格一样,小 ...
- CodeVs.1036 商务旅行 ( LCA 最近公共祖先 )
CodeVs.1036 商务旅行 ( LCA 最近公共祖先 ) 题意分析 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从 ...
- codevs——1036 商务旅行
1036 商务旅行 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 查看运行结果 题目描述 Description 某首都城市的商人要经常 ...
随机推荐
- Canvas 使用及应用
Canvas canvas 是 HTML5 当中我最喜欢的所有新特性中我最喜欢的一个标签了.因为它太强大了,各种有意思的特效都可以实现. 1. canvas 的基本使用方法 - 它是一个行内块元素 - ...
- C#小游戏—钢铁侠VS太空侵略者
身为漫威迷,最近又把<钢铁侠>和<复仇者联盟>系列又重温了一遍,真的是印证了那句话:“读书百遍,其意自现”.看电影一个道理,每看一遍,都有不懂的感受~ 不知道大伙是不是也有同样 ...
- PySide2的This application failed to start because no Qt platform plugin could be initialized解决方式
解决PySide2的This application failed to start because no Qt platform plugin could be initialized问题 今天在装 ...
- Java 读取Word中的脚注、尾注
本文介绍读取Word中的脚注及尾注的方法,添加脚注.尾注可以参考这篇文章. 注:本文使用了Word类库(Free Spire.Doc for Java 免费版)来读取,获取该类库可通过官网下载,并解压 ...
- 利用mnist数据集进行深度神经网络
初始神经网络 这里要解决的问题是,将手写数字的灰度图像(28 像素 x28 像素)划分到 10 个类别中(0~9).我们将使用 MINST 数据集,它是机器学习领域的一个经典数据集,其历史几乎和这个领 ...
- 判断 tableZen 是否有 横向滚动条
判断 tableZen 是否有 横向滚动条 const outWidth = this.$refs.tableInnerZen.$el.clientWidth ].$el.clientWidth
- node.js中http.respone.end方法概述
方法说明: 结束响应,告诉客户端所有消息已经发送.当所有要返回的内容发送完毕时,该函数必须被调用一次. 如果不调用该函数,客户端将永远处于等待状态. 语法: response.end([data], ...
- java第二节课课后
动手动脑问题 : 程序源代码: //MethodOverload.java //Using overloaded methods public class MethodOverload { publi ...
- linux redis安装 5.0.2
参看:https://www.cnblogs.com/limit1/p/9045183.html 1.获取redis资源 wget http://download.redis.io/releases/ ...
- Go语言defer分析
什么是defer? defer语句是专门在函数结束以后做一些清理工作的.我们先举一个例子来更好的理解,现在有一个函数,它的作用是把一个文件内容拷贝到另一个文件. func CopyFile(dstNa ...