LCA(最近公共祖先 Tarjan) CodeVs-2370-小机房的树
2017-11-19 11:18
351 查看
传送门
2370 小机房的树
时间限制: 1 s
空间限制: 256000 KB
题目等级 : 钻石 Diamond
题解
查看运行结果
题目描述 Description
小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上。有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花费太多精力。已知从某个节点爬到其父亲节点要花费 c 的能量(从父亲节点爬到此节点也相同),他们想找出一条花费精力最短的路,以使得搞基的时候精力旺盛,他们找到你要你设计一个程序来找到这条路,要求你告诉他们最少需要花费多少精力
输入描述 Input Description
第一行一个n,接下来n-1行每一行有三个整数u,v, c 。表示节点 u 爬到节点 v 需要花费 c 的精力。
第n+1行有一个整数m表示有m次询问。接下来m行每一行有两个整数 u ,v 表示两只虫子所在的节点
输出描述 Output Description
一共有m行,每一行一个整数,表示对于该次询问所得出的最短距离。
样例输入 Sample Input
3
1 0 1
2 0 1
3
1 0
2 0
1 2
样例输出 Sample Output
1
1
2
数据范围及提示 Data Size & Hint
1<=n<=50000, 1<=m<=75000, 0<=c<=1000
题意:就像上面所说的,一棵树,然后每条边有权值,然后给定两个点,要求找一个点,让其他两个点到那里相聚,目的点可以是给定点的其中一个。然后要你求出最小的距离。
其实我们知道,对于一棵树来说,两个点之间不走回路的路径是唯一的,而且也是最短的。也就是说,选定哪个点做目的点根本不重要,因为在这两个点之间只有唯一的路径,而在这条路径上的目的点造成的路径距离都是一样的。所以只要是这条路径上的点都行。那么问题就与目的点无关了,问题就只变成求树上两个点之间的距离了。
点有 50000 询问有 75000 。
要求两个点之间的距离,得先找到两个点之间的最近公共祖先点 z
做一个距离前缀和,记录的是根节点到当前点的距离(路径唯一)记为 sum[]
那么 两点之间距离 = sum[x] + sum[y] - 2 * sum[z]
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
#define ll long long
#define maxn 50005
#define maxm 75005
#define mem(a,x) memset(a,x,sizeof(a))
struct node {
int v,c;
};
struct ques {
int v,id;
};
vector<node>e[maxn]; // 记录树边
vector<ques>q[maxn]; // 记录与每个点有联系的询问,并记录询问顺序
int ans[maxm],fa[maxn],vis[maxn],sum[maxn],n,m;
int find(int x) { // 并查集
while(x != fa[x]) {
x = fa[x];
}
return x;
}
void dfs(int u,int pre,int val) {
sum[u] = val;
int sz = e[u].size();
for(int i = 0; i < sz; i++) {
int v = e[u][i].v;
int c = e[u][i].c;
if(v == pre) {
continue;
} else {
dfs(v,u,val + c);
fa[v] = u;
}
}
int qsz = q[u].size();
for(int i = 0; i < qsz; i++) {
int v = q[u][i].v;
if(vis[v]) {
int id = q[u][i].id;
int aim = find(v);
ans[id] = sum[u] + sum[v] - 2 * sum[aim];
}
}
vis[u] = 1;
}
void init() {
mem(vis,0);
for(int i = 1; i <= n; i++) {
fa[i] = i;
}
}
int main() {
int u,v,c;
scanf("%d",&n);
init();
for(int i = 1; i < n; i++) {
scanf("%d %d %d",&u,&v,&c);
node tmp;
tmp.v = v,tmp.c = c;
e[u].push_back(tmp);
tmp.v = u;
e[v].push_back(tmp);
}
scanf("%d",&m);
for(int i = 1; i <= m; i++) {
scanf("%d %d",&u,&v);
ques tmp;
tmp.v = v,tmp.id = i;
q[u].push_back(tmp);
tmp.v = u;
q[v].push_back(tmp);
}
dfs(0,-1,0);
for(int i = 1; i <= m; i++) {
printf("%d\n",ans[i]);
}
return 0;
}
/*
11
0 1 3
2 0 6
3 1 2
1 4 4
4 9 7
4 10 9
2 5 5
5 6 5
7 5 3
8 7 2
5
3 2
5 1
7 4
9 6
8 1
a8a7
0
*/
2370 小机房的树
时间限制: 1 s
空间限制: 256000 KB
题目等级 : 钻石 Diamond
题解
查看运行结果
题目描述 Description
小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上。有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花费太多精力。已知从某个节点爬到其父亲节点要花费 c 的能量(从父亲节点爬到此节点也相同),他们想找出一条花费精力最短的路,以使得搞基的时候精力旺盛,他们找到你要你设计一个程序来找到这条路,要求你告诉他们最少需要花费多少精力
输入描述 Input Description
第一行一个n,接下来n-1行每一行有三个整数u,v, c 。表示节点 u 爬到节点 v 需要花费 c 的精力。
第n+1行有一个整数m表示有m次询问。接下来m行每一行有两个整数 u ,v 表示两只虫子所在的节点
输出描述 Output Description
一共有m行,每一行一个整数,表示对于该次询问所得出的最短距离。
样例输入 Sample Input
3
1 0 1
2 0 1
3
1 0
2 0
1 2
样例输出 Sample Output
1
1
2
数据范围及提示 Data Size & Hint
1<=n<=50000, 1<=m<=75000, 0<=c<=1000
题意:就像上面所说的,一棵树,然后每条边有权值,然后给定两个点,要求找一个点,让其他两个点到那里相聚,目的点可以是给定点的其中一个。然后要你求出最小的距离。
其实我们知道,对于一棵树来说,两个点之间不走回路的路径是唯一的,而且也是最短的。也就是说,选定哪个点做目的点根本不重要,因为在这两个点之间只有唯一的路径,而在这条路径上的目的点造成的路径距离都是一样的。所以只要是这条路径上的点都行。那么问题就与目的点无关了,问题就只变成求树上两个点之间的距离了。
点有 50000 询问有 75000 。
要求两个点之间的距离,得先找到两个点之间的最近公共祖先点 z
做一个距离前缀和,记录的是根节点到当前点的距离(路径唯一)记为 sum[]
那么 两点之间距离 = sum[x] + sum[y] - 2 * sum[z]
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
#define ll long long
#define maxn 50005
#define maxm 75005
#define mem(a,x) memset(a,x,sizeof(a))
struct node {
int v,c;
};
struct ques {
int v,id;
};
vector<node>e[maxn]; // 记录树边
vector<ques>q[maxn]; // 记录与每个点有联系的询问,并记录询问顺序
int ans[maxm],fa[maxn],vis[maxn],sum[maxn],n,m;
int find(int x) { // 并查集
while(x != fa[x]) {
x = fa[x];
}
return x;
}
void dfs(int u,int pre,int val) {
sum[u] = val;
int sz = e[u].size();
for(int i = 0; i < sz; i++) {
int v = e[u][i].v;
int c = e[u][i].c;
if(v == pre) {
continue;
} else {
dfs(v,u,val + c);
fa[v] = u;
}
}
int qsz = q[u].size();
for(int i = 0; i < qsz; i++) {
int v = q[u][i].v;
if(vis[v]) {
int id = q[u][i].id;
int aim = find(v);
ans[id] = sum[u] + sum[v] - 2 * sum[aim];
}
}
vis[u] = 1;
}
void init() {
mem(vis,0);
for(int i = 1; i <= n; i++) {
fa[i] = i;
}
}
int main() {
int u,v,c;
scanf("%d",&n);
init();
for(int i = 1; i < n; i++) {
scanf("%d %d %d",&u,&v,&c);
node tmp;
tmp.v = v,tmp.c = c;
e[u].push_back(tmp);
tmp.v = u;
e[v].push_back(tmp);
}
scanf("%d",&m);
for(int i = 1; i <= m; i++) {
scanf("%d %d",&u,&v);
ques tmp;
tmp.v = v,tmp.id = i;
q[u].push_back(tmp);
tmp.v = u;
q[v].push_back(tmp);
}
dfs(0,-1,0);
for(int i = 1; i <= m; i++) {
printf("%d\n",ans[i]);
}
return 0;
}
/*
11
0 1 3
2 0 6
3 1 2
1 4 4
4 9 7
4 10 9
2 5 5
5 6 5
7 5 3
8 7 2
5
3 2
5 1
7 4
9 6
8 1
a8a7
0
*/
相关文章推荐
- 4000 [题解] CodeVS 2370 小机房的树(LCA Tarjan)
- CodeVs.1036 商务旅行 ( LCA 最近公共祖先 )
- CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先)
- 【codevs2370】小机房的树——倍增法/链剖求LCA
- LCA 最近公共祖先——Tarjan(离线)算法的基本思路及其算法实现
- Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)
- Algorithm,LCA,Tarjan,深搜+并查集,最近公共祖先
- LCA最近公共祖先 Tarjan离线算法
- LCA-最近公共祖先-Tarjan解法
- CODE[VS] 2370 小机房的树 暴力求解LCA
- 最近公共祖先(LCA):tarjan与倍增
- c++最近公共祖先LCA(倍增算法和tarjan)
- [LCA][CODEVS 2370]小机房的树
- LCA(最近公共祖先)--tarjan离线算法 hdu 2586
- LCA最近公共祖先(tarjan离线算法)
- LCA最近公共祖先(tarjan离线)
- Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)
- luogu3379 最近公共祖先(LCA) tarjan 倍增
- [转]Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)
- 树上两点的最近公共祖先-Tarjan_LCA离线算法