[usaco2008 Oct]Pasture Walking 牧场旅行

题目描述

n个被自然地编号为1..n奶牛(1<=n<=1000)正在同样被方便的编号为1..n的n个牧场中吃草。更加自然而方便的是,第i个奶牛就在第i个牧场中吃草。 其中的一些对牧场被总共的n-1条双向通道的一条连接。奶牛可以通过通道。第i条通道连接的两个牧场是A_i和B_i(1<=A_i<=N;1<=B_i<=N)其长度是L_i(1<=L_i<=10000)。 通道只会连接两个不同的牧场,所以这些通道使得整个牧场构成了一棵树。 奶牛们是好交际的希望能够经常的访问别的奶牛。急切地,它们希望你能通过告诉它们Q(1<=Q<=1000)对牧场的路径长度来帮助他们安排旅行。(这里将有Q个询问,p1,p2(1<=p1<=n;1<=p1<=n))

输入格式

第1行:两个用空格隔开的整数:n和Q

第2..n行:第i+1行包含三个用空格隔开的整数:A_i,B_i和L_i

第n+1..N+Q行:每行包含两个用空格隔开的整数,代表两个不同的牧场,p1和p2

输出格式

第1..Q行:行i包含第i个询问的答案。


很基础的LCA题。

本题的重点就是计算树上距离。树上距离怎么算?我们可以在线算,也可以离线算。

首先明确思路,dist(u,v)=dist(u,lca(u,v))+dist(lca(u,v),v)。然后我们讨论两种做法:

在线。借助于倍增的做法,用dis(i,j)表示i往上走2^j步走过的距离。那么可以得出以下的递推公式——

[dis[i][j]=dis[i][j-1]+dis[fa[i][j-1]][j-1] ]

初始化dis(i,0)为i与父亲之间边的边长。其中fa(i,j)表示i往上走2^j步到达的点编号。然后对于每次询问,我们倍增地往lca出跳,并累加上跳过的距离,最后累加的值就是答案了。时间复杂度为O((N+Q)logN)。

离线。借助于树上差分的做法,用dis(x)表示x到根的距离,那么dist(u,v)=(dis(u)-dis(lca))+(dis(v)-dis(lca))。lca也可以用离线的Tarjan算法求,Tarjan的过程中顺便就可以处理出dis数组了。时间复杂度为O(N+Q)

附上离线做法的代码:(由于个人习惯,代码中的m就是题目的q)

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 1001
using namespace std;
 
struct edge{
    int to,dis,next;
    edge(){}
    edge(const int &_to,const int &_dis,const int &_next){ to=_to,dis=_dis,next=_next; }
}e[maxn<<1];
int head[maxn],k;
struct question{
    int to,lca,next;
    question(){}
    question(const int &_to,const int &_next){ to=_to,next=_next; }
}q[maxn<<1];
int qhead[maxn],qk;
 
int dis[maxn],fa[maxn];
bool vis[maxn];
int n,m;
 
inline int read(){
    register int x(0),f(1); register char c(getchar());
    while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
    while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
inline void add(const int &u,const int &v,const int &w){ e[k]=edge(v,w,head[u]),head[u]=k++; }
inline void link(const int &u,const int &v){ q[qk]=question(v,qhead[u]),qhead[u]=qk++; }
 
int get(int x){ return x==fa[x]?x:fa[x]=get(fa[x]); }
void tarjan(int u){
    vis[u]=true,fa[u]=u;
    for(register int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(vis[v]) continue;
        dis[v]=dis[u]+e[i].dis;
        tarjan(v),fa[v]=u;
    }
    for(register int i=qhead[u];~i;i=q[i].next){
        int v=q[i].to;
        if(vis[v]) q[i].lca=q[i^1].lca=get(v);
    }
}
 
int main(){
    memset(head,-1,sizeof head),memset(qhead,-1,sizeof qhead);
    n=read(),m=read();
    for(register int i=1;i<n;i++){
        int u=read(),v=read(),w=read();
        add(u,v,w),add(v,u,w);
    }
    for(register int i=1;i<=m;i++){
        int u=read(),v=read();
        link(u,v),link(v,u);
    }
 
    tarjan(1);
    for(register int i=0;i<qk;i+=2){
        int u=q[i].to,v=q[i^1].to,lca=q[i].lca;
        printf("%d
",dis[u]+dis[v]-2*dis[lca]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/akura/p/10940125.html