JIH的玩偶 8.2 T3

题目描述
JIH的玩具厂设立以来,发展了一张销售关系网。这张网以玩具厂为总代理(根),构成一颗树。每个节点都代表一个客户,且每个节点都有重要度ai。JIH想将这些客户划成若干类别,当然同一类的客户重要度相差太大总是不妥。所以JIH决定先进行市场调研。JIH会选择两个客户X,从X向根走一共k个节点进行调查。调查的结果是这条路径上重要程度相差最大的两个客户的差值是多少。因为特殊需要,要求重要度大的客户必须在重要度小的客户后面(顺序为X到根,若序列为递减,则输出0,详情见样例)。

输入
第一行一个整数N 表示N个客户
第二行N个整数Ai 表示N个客户的重要程度(工厂是1)
第三行开始 共N-1行 每行2个整数 x,y 表示x的父亲是y
接着一行一个正整数Q,表示Q次调研
接着Q行,每行两个整数X,K。含义见题目表述。

输出
Q行,每行一个正整数,含义见题目描述。

样例输入
6
5 6 1 7 5 2
2 1
3 1
4 2
5 2
6 3
3
4 3
6 2
6 3
样例输出
0
0
4
提示
数据范围:
30% 的数据中N,Q<=1000
100%的数据中N,Q<=200000 Ai<=1000000

这题的意思就是说,有q个询问,在x往根走k-1步的这条路上,最大的差值,且大的比小的离根更近。
我们可以用倍增法递推出所需的
max[i][j],min[i][j],ans[i][j]i2j
因为要保证大的离根更近,所以ans的递推需要这样:

 for(int i=1;(1<<i)<=n;i++) 
    for(int j=1;j<=n;j++) 
    {
        f[j][i]=f[f[j][i-1]][i-1]; 
        Max[j][i]=max(Max[j][i-1],Max[f[j][i-1]][i-1]);
        Min[j][i]=min(Min[j][i-1],Min[f[j][i-1]][i-1]);
        Ans[j][i]=max(Max[f[j][i-1]][i-1]-Min[j][i-1],max(Ans[j][i-1],Ans[f[j][i-1]][i-1])); //拆成两个区间a,b,再与b中max与a中min的差值进行比较
    }

那么怎么处理询问呢?记得RMQ的话,就肯定知道它处理询问是由两个重叠的区间max或min产生最终答案的,我们也可以用这种方法,把询问的区间,拆成[a,c]、[b,d]两个区间
这样就有了一条线段 a b c d
再注意前面ans数组的定义,它只是当前区间内的最大差,这里还有一种情况,就是[c,d]中的max-[a,b]中的min,需要对这种情况再进行讨论。代码写得有点繁琐,领会思想就行。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n,q,x,y,z,t,w,l,r,tot,ans;
int a[200005],deep[200005],head[200005],Next[400005],to[400005];
int f[200005][18],Max[200005][18],Min[200005][18],Ans[200005][18];
void dfs(int k,int pre)
{
    deep[k]=deep[pre]+1;
    for(int i=head[k];i!=-1;i=Next[i]) 
    if(to[i]!=pre) 
    {
        f[to[i]][0]=k;
        Max[to[i]][0]=max(a[k],a[to[i]]);
        Min[to[i]][0]=min(a[k],a[to[i]]);
        Ans[to[i]][0]=max(0,a[k]-a[to[i]]);
        dfs(to[i],k);
    }
}
void add(int x,int y)
{
    tot++;
    Next[tot]=head[x];
    to[tot]=y;
    head[x]=tot;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) head[i]=-1;
    for(int i=1;i<n;i++) 
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs(1,0);
    for(int i=1;(1<<i)<=n;i++) 
    for(int j=1;j<=n;j++) 
    {
        f[j][i]=f[f[j][i-1]][i-1]; 
        Max[j][i]=max(Max[j][i-1],Max[f[j][i-1]][i-1]);
        Min[j][i]=min(Min[j][i-1],Min[f[j][i-1]][i-1]);
        Ans[j][i]=max(Max[f[j][i-1]][i-1]-Min[j][i-1],max(Ans[j][i-1],Ans[f[j][i-1]][i-1]));
    }
    cin>>q;
    for(int i=1;i<=q;i++) 
    {
        scanf("%d%d",&x,&y);
        y--;
        if(y==0) 
        {
            printf("0\n");
            continue;
        }
        t=y;
        z=0;
        while(t>0) 
        {
            z++;
            t=t/2;
        }
        z--;
        ans=Ans[x][z]; //第一个区间 
        t=y-(1<<z);w=x;
        for(int j=17;j>=0;j--) 
        if((t&(1<<j))==(1<<j)) w=f[w][j];
        ans=max(ans,Ans[w][z]); //第二个区间     x,w,x+(1<<z),w+(1<<z)
        if(z>0) 
        {
            int v=0;
            l=f[x][z];
            r=f[w][z];
            for(int j=17;j>=0;j--) 
            if(deep[f[l][j]]>=deep[r]) 
            {
                v=max(v,Max[l][j]);
                l=f[l][j];
            }
            int o=1e9;
            l=x;
            r=w;
            for(int j=17;j>=0;j--) 
            if(deep[f[l][j]]>=deep[r]) 
            {
                o=min(o,Min[l][j]);
                l=f[l][j];
            }
            ans=max(ans,v-o);
        }
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值