任意门:http://codevs.cn/problem/2370/
题目描述 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
题意概括:
需要注意一点是给的边的信息是先给子节点再给父亲结点。
解题思路:
DFS预处理各个结点到树的根结点的距离(如果建双向边要判重)
Tarjan 找出最近公共祖先,老套路(如果建双向边要判重)
TIP:
这些题目要注意找根结点(建单向边的时候),如果搞不清楚谁是子节点谁是父亲结点,建双向边,操作时判重一下即可。
AC code:
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #define INF 0x3f3f3f3f 7 #define LL long long 8 using namespace std; 9 const int MAX_N = 5e4+5; 10 const int MAX_M = 8e4; 11 struct Edge{int v, w, nxt;}edge[MAX_N<<1]; 12 struct Query 13 { 14 int v, id; 15 Query(){}; 16 Query(int _v,int _id):v(_v),id(_id){}; 17 }; 18 vector<Query> q[MAX_N]; 19 20 int head[MAX_N], cnt; 21 int dis[MAX_N]; 22 int fa[MAX_N]; 23 bool vis[MAX_N]; 24 bool in[MAX_N]; 25 int ans[MAX_M]; 26 int N, M; 27 28 void init() 29 { 30 memset(head, -1, sizeof(head)); 31 memset(dis, 0, sizeof(dis)); 32 memset(vis, false, sizeof(vis)); 33 memset(in, false, sizeof(in)); 34 memset(ans, 0, sizeof(ans)); 35 cnt = 0; 36 } 37 38 void AddEdge(int from, int to, int weight) 39 { 40 edge[cnt].v = to; 41 edge[cnt].w = weight; 42 edge[cnt].nxt = head[from]; 43 head[from] = cnt++; 44 } 45 46 int getfa(int x){return fa[x]==x?x:fa[x] = getfa(fa[x]);} 47 /* 48 int getfa(int x) //找祖先 49 { 50 int root = x; 51 while(fa[root] != root) root = fa[root]; 52 53 int tmp; 54 while(fa[x] != root){ 55 tmp = fa[x]; 56 fa[x] = root; 57 x = tmp; 58 } 59 return root; 60 } 61 */ 62 void dfs(int s, int f) 63 { 64 for(int i = head[s]; i != -1; i = edge[i].nxt){ 65 //if(edge[i].v == f) continue; //如果建双向边要去重 66 dis[edge[i].v] = dis[s] + edge[i].w; 67 dfs(edge[i].v, s); 68 } 69 } 70 71 void Tarjan(int s, int f) 72 { 73 int root = s; 74 fa[s] = s; 75 for(int i = head[s]; i != -1; i = edge[i].nxt){ 76 int Eiv = edge[i].v; 77 //if(Eiv == f) continue; //如果建双向边要去重 78 Tarjan(Eiv, root); 79 fa[getfa(Eiv)] = s; 80 } 81 vis[s] = true; 82 for(int i = 0; i < q[s].size(); i++){ 83 if(vis[q[s][i].v] && !ans[q[s][i].id]){ 84 ans[q[s][i].id] = dis[q[s][i].v] + dis[s] - 2*dis[getfa(q[s][i].v)]; 85 } 86 } 87 } 88 89 int main() 90 { 91 init(); 92 scanf("%d", &N); 93 for(int i = 1, u, v, w; i < N; i++){ 94 //scanf("%d %d %d", &u, &v, &w); //建双向边 95 scanf("%d %d %d", &v, &u, &w); 96 AddEdge(u, v, w); 97 //AddEdge(v, u, w); //建双向边 98 in[v] = true; 99 } 100 scanf("%d", &M); 101 for(int i = 1, u, v; i <= M; i++){ 102 scanf("%d %d", &u, &v); 103 q[u].push_back(Query(v, i)); 104 q[v].push_back(Query(u, i)); 105 } 106 107 int root = 0; 108 for(int i = 0; i < N; i++) if(!in[i]){root = i; break;} 109 110 dfs(root, -1); 111 Tarjan(root, -1); 112 113 for(int i = 1; i <= M; i++){ 114 printf("%d ", ans[i]); 115 } 116 return 0; 117 }