[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 某首都城市的商人要经常 ...
随机推荐
- 零基础JavaScript编码(三)总结
任务目的 在上一任务基础上继续JavaScript的体验 接触一下JavaScript中的高级选择器 学习JavaScript中的数组对象遍历.读写.排序等操作 学习简单的字符串处理操作 任务描述 参 ...
- Linux中软连接和硬连接的区别
首先,我们要清楚符号链接的目的,在不改变原目录/文件的前提下,起一个方便的别名(在这起个别名,让我想到前阶段学C里typedef也是起别名的). 1.软连接就相当于windows的快捷方式.例如:ln ...
- apache搭建Tomcat集群(Cluster)
搭建集群: apache:特点处理静态资源(html 图片 js等) apache的请求操作,Cluster工具 tomcat:特点处理动态资源 apache+tomcat(apache是web服 ...
- 有关vue中用element ui 中的from表单提交json格式总是有冒号的问题解决办法
因为后台要求要传递JSON格式的数据给他,然后我转了之后总是多了冒号,后来又看了自己的报错,原来是报了404错误,说明路径找不到, 数据格式 后来发现怎么都不行了,然后突然查看了报错报的是404,说明 ...
- 基于osg的python三维程序开发(三)------几何形体及纹理
def createScene(): geode = osg.Geode() pointsGeom = osg.Geometry() vertices = osg.Vec3Array() vertic ...
- [android]p7-1 fragment学习笔记
本文源自<android权威编程指南第3版>第7章UI fragment与fragment 第7章主要内容是实现一个记录不良行为的APP(部分实现),有列表,有具体的行为内容显示.第7章主 ...
- 结巴分词demo
#encoding=utf-8 from __future__ import unicode_literals import sys sys.path.append("../") ...
- 【原创】Java并发编程系列1:大纲
[原创]Java并发编程系列1:大纲 一个人能力当中所蕴藏的潜能,远超过自己想象以外. 为什么要学习并发编程 随着现今互联网行业的迅猛发展,其业务复杂度.并发量也在不断增加,对程序的要求变得越来越高, ...
- (转)协议森林04 地址耗尽危机 (IPv4与IPv6地址)
协议森林04 地址耗尽危机 (IPv4与IPv6地址) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! IP地址是IP协议的重要组 ...
- 简说Python之Jupyter Notebook
目录 简说Python之Jupyter Notebook 1.Jupyter Notebook 系统环境:Ubuntu 18.04.1 LTS Python使用的是虚拟环境:virutalenv Py ...