【codevs3305】水果姐逛水果街Ⅱ LCA

题目描述 Description

水果姐第二天心情也很不错,又来逛水果街。

突然,cgh又出现了。cgh施展了魔法,水果街变成了树结构(店与店之间只有一条唯一的路径)。

同样还是n家水果店,编号为1~n,每家店能买水果也能卖水果,并且同一家店卖与买的价格一样。

cgh给出m个问题,每个问题要求水果姐从第x家店出发到第y家店,途中只能选一家店买一个水果,然后选一家店(可以是同一家店,但不能往回走)卖出去。求最多可以赚多少钱。

水果姐向学过oi的你求助。

输入描述 Input Description

第一行n,表示有n家店

下来n个正整数,表示每家店一个苹果的价格。

下来n-1行,每行两个整数x,y,表示第x家店和第y家店有一条边。

下来一个整数m,表示下来有m个询问。

下来有m行,每行两个整数x和y,表示从第x家店出发到第y家店。

输出描述 Output Description
有m行。

每行对应一个询问,一个整数,表示面对cgh的每次询问,水果姐最多可以赚到多少钱。

样例输入 Sample Input

10
16 5 1 15 15 1 8 9 9 15 
1 2
1 3
2 4
2 5
2 6
6 7
4 8
1 9
1 10
6
9 1
5 1
1 7
3 3
1 1
3 6

样例输出 Sample Output

7
11
7
0
0
15

数据范围及提示 Data Size & Hint

0<=苹果的价格<=10^8

0 < n<=200000

0 < m<=10000


不得不说这个lca真的好恶心!!

记录不知道多少个值:

anc[x][i]x往上(1<<i)的祖先。
maxn[x][i]xanc[x][i]的最大值。
minn[x][i]xanc[x][i]的最小值。
ans1[x][i]:从anc[x][i]走到x的答案。
ans2[x][i]:从x走到anc[x][i]的答案。

假设路线是x->y,并x在lca左边,y在lca右边。
这样,答案只会在:

lca左边 
lca右边
lca右边的最大值-lca左边的最小值

对于左边,要时刻用未来的最大值-历史的最小值更新答案。
对于右边,要时刻用历史的最大值-当前的最小值更新答案。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int size=400010;
const int INF=233333333;
int head[size],nxt[size],to[size],tot=0;

int num[size];

int maxn[size][30],minn[size][30],anc[size][30],ans1[size][30],ans2[size][30];
//ans1:从上到下  ans2:从下到上
int deep[size];

void build(int f,int t)
{
    to[++tot]=t;
    nxt[tot]=head[f];
    head[f]=tot;
}

void dfs(int u,int fa)
{
    if(deep[u]) return ;
    deep[u]=deep[fa]+1;
    minn[u][0]=min(num[u],num[fa]);
    maxn[u][0]=max(num[u],num[fa]);
    ans1[u][0]=max(0,num[u]-num[fa]);
    ans2[u][0]=max(0,num[fa]-num[u]);
    anc[u][0]=fa;
    for(int i=1;anc[u][i-1];i++)
    {
        anc[u][i]=anc[anc[u][i-1]][i-1];
        maxn[u][i]=max(maxn[u][i-1],maxn[anc[u][i-1]][i-1]);
        minn[u][i]=min(minn[u][i-1],minn[anc[u][i-1]][i-1]);    
        ans1[u][i]=max( max(ans1[u][i-1],ans1[anc[u][i-1]][i-1]) , maxn[u][i-1]-minn[anc[u][i-1]][i-1] );
        ans2[u][i]=max( max(ans2[u][i-1],ans2[anc[u][i-1]][i-1]) , maxn[anc[u][i-1]][i-1]-minn[u][i-1] );
    }
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        dfs(v,u);
    }
}

int asklca(int x,int y)
{
    int ans=0;
    if(deep[x]<deep[y]) swap(x,y);
    if(deep[x]>deep[y])
    {
        int dd=deep[x]-deep[y];
        for(int i=24;i>=0;i--)
        {
            if(dd&(1<<i))
            {
                x=anc[x][i];
            }
        }
    }
    if(x!=y)
    {
        for(int i=24;i>=0;i--)
        {
            if(anc[x][i]!=anc[y][i])
            {
                x=anc[x][i];
                y=anc[y][i];
            }
        }       
    }
    if(x==y) return x;
    else return anc[x][0];
}
int ask(int x,int y)
{
    int lca=asklca(x,y);
    int maxx=-INF,minnn=INF,ans=0;

    int dd1=deep[x]-deep[lca];
    if(dd1>0)
    {
        for(int i=24;i>=0;i--)
        {
            if(dd1&(1<<i))
            {
                ans=max(ans, max(ans2[x][i],maxn[x][i]-minnn) );
                minnn=min(minn[x][i],minnn);
                x=anc[x][i];
            }
        }
    }

    int dd2=deep[y]-deep[lca];
    if(dd2>0)
    {
        for(int i=24;i>=0;i--)
        {
            if(dd2&(1<<i))
            {
                ans=max(ans, max(ans1[y][i],maxx-minn[y][i]) );
                maxx=max(maxn[y][i],maxx);
                y=anc[y][i];
            }
        }   
    }
    return max(ans,maxx-minnn);
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
    }
    for(int i=1;i<=n-1;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        build(a,b); build(b,a);
    }   
    dfs(1,0);
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",ask(x,y));
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值