[BZOJ2684][USACO 2004 FEB]距离询问

13 篇文章 0 订阅
6 篇文章 0 订阅
题目描述
FJ有N(2 <= N <= 40,000)个农场,编号从1..N。有M(1 <= M < 40,000)条水平或者垂直的道路连接这些农场(1 <= length <= 1000)。下图是有7个农场的例子,括号中的数字表示道路的长度:
           F1 --- (13) ---- F6 --- (9) ----- F3
            |                                 |
           (3)                                 |
            |                                 (7)
           F4 --- (20) -------- F2             |
            |                                   |
           (2)                                F5
            | 
           F7 
每个农场最多只与它上、下、左、右的农场相连。任意两个农场之间,只有唯一的一条路径。

现在给出K个询问,形如:X  Y,表示请你回答编号为X的农场与编号为Y的农场之间的路径长度


输入
 第1行:2个整数 N 和 M,意义如前所述
接下来M行,每行4个变量F1, F2, L, D 描述一条道路, F1 和  F2 是这条道路所连接的两个农场的编号, L 是这条道路的长度, D 是这条道路从F1到F2的方向,即 'N', 'E', 'S', 'W' 这4个字母之一,分别表示北,东,南,西
接下来1行上有1个整数K,表示询问的次数K (1 <= K <= 10,000)

接下来K行,每行2个整数X,Y,表示询问X和Y之间的最短距离


输出

对每个询问,在一个单独的行上,用一个整数回答


样例输入
7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S
3
1 6
1 4

2 6


样例输出
13
3

36


提示

Farms 2 and 6 are 20+3+13=36 apart.



题解:LCA半裸题,求LCA的过程中同步记录路径最小值


倍增版:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=40010;
const int LOG=30;
 
 
int n, m, k;
void Getin( int &shu ) {
    char c; int f=1; shu=0;
    for( c=getchar(); c<'0' || c>'9'; c=getchar() ) if( c=='-' ) f=-1;
    for( ; c>='0' && c<='9'; c=getchar() ) shu=shu*10+c-'0';
    shu*=f;
}
 
 
int fir[N], ecnt;
struct node{ int e, w, next; }edge[N<<1];
void Link( int s, int e, int w ) {
    edge[++ecnt].e=e; edge[ecnt].w=w;
    edge[ecnt].next=fir[s]; fir[s]=ecnt;
    edge[++ecnt].e=s; edge[ecnt].w=w;
    edge[ecnt].next=fir[e]; fir[e]=ecnt;
}
 
 
int fa[N][LOG+5], dep[N], dis[N];
void DFS( int r, int f, int d, int l ) {
    dep[r]=d; dis[r]=l;
    for( int i=fir[r]; i; i=edge[i].next )
        if( edge[i].e!=f ) {
            fa[edge[i].e][0]=r;
            DFS( edge[i].e, r, d+1, l+edge[i].w );
        }
}
 
void Build() {
    DFS( 1, 0, 1, 0 );
    for( int j=1; j<=LOG; j++ )
        for( int i=1; i<=n; i++ )
            fa[i][j]=fa[ fa[i][j-1] ][j-1];
}
 
 
int getk( int r ,int k ) {
    for( int i=0; i<=LOG; i++ )
        if( k&( 1<<i ) ) r=fa[r][i];
    return r;
}
 
int getd( int r, int d ) { return getk( r, dep[r]-d ); }
 
int LCA_Find( int s1, int s2 ) {
    if( dep[s1]<dep[s2] ) swap( s1, s2 );
    s1=getd( s1, dep[s2] );
    if( s1==s2 ) return s1;
    for( int i=LOG; i>=0; i-- )
        if( fa[s1][i]!=fa[s2][i] )
            s1=fa[s1][i], s2=fa[s2][i];
    return fa[s1][0];
}
 
 
int p1, p2;
int Solve() {
    int f=LCA_Find( p1, p2 );
    return dis[p1]+dis[p2]-dis[f]*2;
}
 
 
int s, e, w;
char c[5];
int main() {
    Getin(n); Getin(m);
    for( int i=1; i<=m; i++ ) {
        Getin(s); Getin(e); Getin(w);
        Link( s, e, w );
        scanf( "%s", c );
    }
     
    Build();
     
    Getin(k);
    for( int i=1; i<=k; i++ ) {
        Getin( p1 ); Getin( p2 );
        printf( "%d\n", Solve() );
    }
    return 0;
}


链剖版:

#include<iostream> 
#include<cstring> 
#include<cstdlib> 
#include<cstdio> 
#include<cmath> 
#include<algorithm> 
using namespace std; 
const int N=40010; 
 
   
int n, m, k, root; 
void Getin( int &shu ) { 
    char c; int f=1; shu=0; 
    for( c=getchar(); c<'0' || c>'9'; c=getchar() ) if( c=='-' ) f=-1; 
    for( ; c>='0' && c<='9'; c=getchar() ) shu=shu*10+c-'0'; 
    shu*=f; 
} 
   
   
int fir[N], ecnt; 
struct node{ int e, w, next; }edge[N<<1]; 
void Link( int s, int e, int w ) { 
    edge[++ecnt].e=e; edge[ecnt].w=w;
    edge[ecnt].next=fir[s]; fir[s]=ecnt;
    edge[++ecnt].e=s; edge[ecnt].w=w;
    edge[ecnt].next=fir[e]; fir[e]=ecnt;
} 
   
 
int fa[N], dep[N], siz[N], son[N], dis[N];
void DFS1( int r, int f, int d, int l ) {
    dep[r]=d; siz[r]=1; dis[r]=l;
    for( int i=fir[r]; i; i=edge[i].next )
        if( edge[i].e!=f ) {
            DFS1( edge[i].e, r, d+1, l+edge[i].w );
            siz[r]+=siz[ edge[i].e ];
            if( siz[ edge[i].e ]>siz[ son[r] ] ) son[r]=edge[i].e;
            fa[ edge[i].e ]=r;
        }
}
 
int top[N];
void DFS2( int r, int fa ) {
    if( son[r] && son[r]!=r ) top[ son[r] ]=top[r], DFS2( son[r], r );
    for( int i=fir[r]; i; i=edge[i].next )
        if( edge[i].e!=fa && edge[i].e!=son[r] ) {
            top[ edge[i].e ]=edge[i].e;
            DFS2( edge[i].e, r );
        }
}
   
int LCA( int p1, int p2 ) {
    while( top[p1]!=top[p2] ) {
        if( dep[ top[p1] ]>dep[ top[p2] ] ) p1=fa[ top[p1] ];
        else p2=fa[ top[p2] ];
    }
    return dep[p1]<dep[p2] ? p1 : p2;
}
   
   
int p1, p2; 
int Solve() { 
    int f=LCA( p1, p2 ); 
    return dis[p1]+dis[p2]-dis[f]*2; 
} 
   
   
int s, e, w; 
char c[5]; 
int main() { 
    Getin(n); Getin(m); root=1;
    for( int i=1; i<=m; i++ ) { 
        Getin(s); Getin(e); Getin(w); 
        Link( s, e, w ); 
        scanf( "%s", c ); 
    } 
     
    DFS1( root, -1, 1, 0 );
    top[root]=root; DFS2( root, -1 );
     
    Getin(k); 
    for( int i=1; i<=k; i++ ) { 
        Getin( p1 ); Getin( p2 ); 
        printf( "%d\n", Solve() ); 
    } 
    return 0; 
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值