POJ 1986 Tarjan离线算法(最近公共祖先)

来源:互联网 发布:mac os sierra剪贴板 编辑:程序博客网 时间:2024/05/20 22:39

题意就是求两个点之间的最短距离 那个字母是没有用的

#include<iostream>#include<cstdio>#include<algorithm>#include<vector>using namespace std;const int N=40010;vector<int> Q[N],num[N];//定义容器 Q[0]为一个容器 Q[1]为一个容器以此类推 每个容器都类似于一个数组 只是需要多大能开多大 节约内存(大概是这样我也不是很懂) int fa[N],d[N],LCA[N],qx[N],qy[N],n,m,g;int Begin[N],Next[N*2],to[N*2],w[N*2],e;void Add(int x,int y,int k){//链式前向星(这个知道的应该很多吧)to[i]是第i条边的目标点 w[i]是第i条边的长度 next[i]指与第i条边的起始点相同的下一条边的编号 begin[j]指输入的最后一条以j为起始点的边的编号即从j点开始遍历的第一条边to[++e]=y;w[e]=k;Next[e]=Begin[x];//至于最后两行为什么这样写 画个图就知道了很好理解 Begin[x]=e;}//链接矩阵需要n*n的数组 链接表需要n*m的数组 前向星只需要几个m的一维数组 最节约内存 void Addq(int x, int y, int k){Q[x].push_back(y);//压入 num[x].push_back(k);//Q[x]是以x为终点的询问的起点的集合 num[x]是以x为起点的询问的编号的集合 }int find(int x){return x==fa[x]?x:(fa[x]=find(fa[x]));}void Tarjan(int h, int dis){fa[h]=h;//每个节点的父亲节点设为自己 d[h]=dis;//d[h]代表从h点到根节点即到1点的距离 for(int i=Begin[h];i;i=Next[i]){//链式前向星的应用 终止条件为编号变成0即后面没边了 int v=to[i];if(!fa[v]){Tarjan(v,dis+w[i]);fa[v]=h;//回溯完再将这个点的父亲节点设置为父亲 自己模拟一下就知道为什么了}}for(int i=0;i<=(int)Q[h].size()-1;i++){//因为h点正在遍历 所以只要询问中有一点为h点并且另一点已经遍历过就可以找他们的最近公共祖先了 int v = Q[h][i];if(fa[v])LCA[num[h][i]]=find(v);//LCA[i]指第i个询问(注意是询问)的两个点的最近公共祖先 }}int main(){scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int x,y,z;scanf("%d%d%d%c%c",&x,&y,&z);Add(x,y,z);Add(y,x,z);}scanf("%d",&g);for(int i=1;i<=g;i++){scanf("%d%d",&qx[i],&qy[i]);Addq(qx[i],qy[i],i);Addq(qy[i],qx[i],i);}Tarjan(1,0);//从根节点开始遍历 1是根节点的编号 0是离根节点的距离 过程类似于dfs for(int i=1;i<=g;i++)printf("%d\n",d[qx[i]]+d[qy[i]]-2*d[LCA[i]]);;return 0;}


Sample Input

7 61 6 13 E6 3 9 E3 5 7 S4 1 3 N2 4 20 W4 7 2 S31 61 42 6

Sample Output


13336
0 0