【题】【倍增】NKOJ 1536 奶牛串门【Usaco Oct08 Gold】(Pasture Walking)

4 篇文章 0 订阅

NKOJ 1536 【Usaco Oct08 Gold】奶牛串门(Pasture Walking)
时间限制 : 30000 MS 空间限制 : 65536 KB
问题描述
有N (2 <= N <= 1,000)头奶牛,分别编号为1到N。还有N颗牧草分别编号为1到N。简单起见,第i头奶牛都盯着第i颗牧草。
有几对牧草分别用一些小路连接了起来,总共有N-1条双向的小路,小路i连接了Ai及Bi颗牧草(1 <= Ai <= N; 1 <= Bi <= N),小路的长度为Li (1 <= Li <= 10,000)保证任意两颗牧草总能通过小路走到,也就是说这些小路构成了一棵树。
奶牛是群居性的动物,很喜欢串门。奶牛们会问你Q次问题(1 <= Q <= 1,000),每次询问的内容很简单,就是从p1颗牧草到p2颗牧草最短的距离是多少。(1 <= p1 <= N; 1 <=p2 <= N)

输入格式
第一行,两个用空格分隔的整数:N和Q
第二行到第N行,第i+1行包含三个用空格分隔的整数Ai,Bi,Li
第N+1行到第N+Q行,每行包含两个用空格分隔的整数p1,p2

输出格式
共Q行,每行包含每次询问的最短距离的值。

样例输入 1
4 2
2 1 2
4 3 2
1 4 3
1 2
3 2

样例输出 1
2
7

样例输入 2
8 4
6 4 5
1 4 3
7 5 5
5 2 3
3 6 4
2 6 2
8 6 8
1 6
8 5
6 4
6 2

样例输出 2
8
13
5
2

提示
样例解释:
询问1:1->2 总代价为2
询问2:3->4->1->2 总代价为7

来源 Usaco October 2008 Gold

优化:距离也倍增求

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int need=1003;
const int inf=1e9;

int len[need*2],la[need*2],en[need*2],fi[need];
int dep[need],fa[need][need],dis[need][need];
int m=0,d=-inf;
bool use[need];

void add(int a,int b,int c)
{
    m++;
    en[m]=b;
    len[m]=c;
    la[m]=fi[a];
    fi[a]=m;
}

void dfs(int s)
{
    int k=ceil(log(dep[s])/log(2));
    d=max(k,d);
    for(int i=1;i<=k;i++) 
    {
        fa[s][i]=fa[fa[s][i-1]][i-1];
        dis[s][i]=dis[s][i-1]+dis[fa[s][i-1]][i-1];
    }
    int t=fi[s],y;
    while(t)
    {
        y=en[t];
        if(use[y]) 
        {
            t=la[t];
            continue ;
        }
        use[y]=true;
        fa[y][0]=s;
        dis[y][0]=len[t];
        dep[y]=dep[s]+1;
        dfs(y);
        t=la[t];
    }
}

void go_up(int &v,int p)
{
    for(int i=0;i<=d;i++) if(p&(1<<i)) v=fa[v][i];
}

int lca(int u,int v)
{
    if(dep[u]>dep[v]) swap(v,u);
    go_up(v,dep[v]-dep[u]);
    if(u==v) return v;
    for(int i=d;i>=0;i--) if(fa[u][i]!=fa[v][i])  v=fa[v][i],u=fa[u][i];
    return fa[v][0];
}

int ans(int a,int b)
{
    if(a==b) return 0;
    int ans=0,p=dep[a]-dep[b];
    for(int i=0;i<=d;i++) 
     if(p&(1<<i)) 
     {
        ans+=dis[a][i];
        a=fa[a][i];
     }
    return ans;
}

int main()
{
    int n,q;scanf("%d%d",&n,&q);
    for(int i=1,a,b,c;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c),add(b,a,c);
    }
    use[1]=true;dep[1]=1;dfs(1);
    for(int i=1,a,b,c;i<=q;i++)
    {
        scanf("%d%d",&a,&b);
        c=lca(a,b);
        printf("%d\n",ans(a,c)+ans(b,c));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值