LCA+RMQ求树中两点距离

来源:互联网 发布:网络分线器怎么用 编辑:程序博客网 时间:2024/05/16 17:15

题目

H:Distance Queries

  • 查看
  • 提交
  • 统计
  • 提问
总时间限制: 
2000ms 
单个测试点时间限制: 
1000ms 
内存限制: 
65536kB
描述

Farmer John's cows refused to run in his marathon since he chose a path much too long for their leisurely lifestyle. He therefore wants to find a path of a more reasonable length. The input to this problem consists of the same input as in "Navigation Nightmare",followed by a line containing a single integer K, followed by K "distance queries". Each distance query is a line of input containing two integers, giving the numbers of two farms between which FJ is interested in computing distance (measured in the length of the roads along the path between the two farms). Please answer FJ's distance queries as quickly as possible!

输入
* Line 1: Two space-separated integers: N and M

* Lines 2..1+M: Each line contains four space-separated entities, F1, F2, L, and D that describe a road. F1 and F2 are numbers of two farms connected by a road, L is its length, and D is a character that is either 'N', 'E', 'S', or 'W' giving the direction of the road from F1 to F2

* Line 2+M: A single integer, K. 1 <= K <= 10,000

* Lines 3+M..2+M+K: Each line corresponds to a distance query and contains the indices of two farms.
(1 <= M < 40,000)
输出
* Lines 1..K: For each distance query, output on a single line an integer giving the appropriate distance.
样例输入
7 61 6 13 E6 3 9 E3 5 7 S4 1 3 N2 4 20 W4 7 2 S31 61 42 6
样例输出
13336
提示
Farms 2 and 6 are 20+3+13=36 apart.


/*DFS + RMQ 在线算法求树中两点之间的距离 */#include<iostream>#include<stdio.h>#include<stdlib.h>#include<vector>#include<queue>#include<string>#include<string.h>#include<map>#include<math.h>#define N 100100using namespace std;struct node{int x,d,next;}edge[2*N];int tot;int head[N];//vector<node> G[N];int E[2*N];//dfs时记录的节点,每条边记录2遍,共记录2*N-1个节点 int D[2*N];//表示dfs记录路径的各个点的深度 int first[N];//first[i]表示记录路径中第一次访问i点的位置下标 int vis[N];//是否访问过 int dis[N];//记录距离根节点的距离 int n,m,top;//top表示数组下标 int dp[30][2*N];//dp[i][j]表示从j点开始,数2^i个点区间内的最大值或最小值,在此题中记录的为最小深度 bool flag[N];void addedge(int u,int v, int w){edge[tot].x=v;edge[tot].d=w;edge[tot].next=head[u];head[u]=tot++;}void dfs(int u,int dep, int w)//正在访问的点u,该点的深度dep,距离根节点的最短路径w {vis[u]=1; E[top]=u;D[top]=dep;first[u]=top++;dis[u]=w;for(int i=head[u];i!=-1;i=edge[i].next){if(!vis[edge[i].x]){int v=edge[i].x;int cost=edge[i].d;dfs(v,dep+1,w+cost);E[top]=u;D[top++]=dep;//回溯时再次访问该点,记录下来     }}/* for(int i=0;i<G[u].size();i++){if(!vis[G[u][i].x]){int v=G[u][i].x;int cost=G[u][i].d;dfs(v,dep+1,w+cost);E[top]=u;D[top++]=dep;//回溯时再次访问该点,记录下来 }}*/ }void ST(int num){//动态规划记录各个区间,及两个点的最近公共祖先所在位置即E和D的下标 for(int i=1;i<=num;i++)    dp[0][i]=i;//当长度为 1,起点为 i 时,最近公共祖先为本身//外循环必须为区间长度,先求出区间长度为1的值才能求出区间长度为2的值,依次类推//内循环为起点 for(int i=1;(1<<i)<=num;i++){for( int j=1;j<=num;j++){if(j+(1<<i)-1 <= num)//区间的右端点必须不大于最大值,1<<i (i=0,1,2,3...时,该值为1,2,4,8... ){int a=dp[i-1][j];int b=dp[i-1][j+(1<<i>>1)];//右半个区间,1<<i>>1相当于2^(i-1)if(D[a]<D[b])     dp[i][j]=a;else    dp[i][j]=b;     }} } } int RMQ(int x,int y){//查询 :求区间x和y之间的最近公共祖先所在位置 //因为区间长度永远是2^i,所以要将查询区间先进行划分 //int k=(int) log(y-x+1.0)/log(2);int k=0;while(1<<(k+1)<=y-x+1) k++;//注意这个地方!!! int a=dp[k][x];int b=dp[k][y-(1<<k)+1];if(D[a]<D[b])    return a;return b; }int main(){while(scanf("%d%d",&n,&m)!=EOF){memset(dis,0,sizeof(dis));memset(first,0,sizeof(first));memset(E,0,sizeof(E));memset(D,0,sizeof(D));memset(vis,0,sizeof(vis));memset(head,-1,sizeof(head));memset(flag,0,sizeof(flag));memset(dp,0,sizeof(dp));tot=0;top=0;    int a,b,w;    char ch;    //node tmp;    while(m--)    {    scanf("%d%d%d %c",&a,&b,&w,&ch);    /*tmp.x=b;    G[a].push_back(tmp);    tmp.x=a;    G[b].push_back(tmp);    */ addedge(a,b,w);addedge(b,a,w);    flag[b]=1;    //无向图 }int root;for(int i=1;i<=n;i++){if(!flag[i]){    root=i;    break;}}dfs(root,0,0);ST(top-1);int k;scanf("%d",&k);while(k--){scanf("%d%d",&a,&b);int x=first[a];int y=first[b];if(x>y)    swap(x,y);int pos=RMQ(x,y);printf("%d\n",dis[a]+dis[b]-2*dis[E[pos]]);} }return 0;}



0 0
原创粉丝点击