[美团 CodeM 复赛]城市网络

题目描述

有一个树状的城市网络(即 nnn 个城市由 n−1n-1n−1 条道路连接的连通图),首都为 111 号城市,每个城市售卖价值为 aia_ia​i​​ 的珠宝。

你是一个珠宝商,现在安排有 qqq 次行程,每次行程为从 uuu 号城市前往 vvv 号城市(走最短路径),保证 vvv 在 uuu 前往首都的最短路径上。

在每次行程开始时,你手上有价值为 ccc 的珠宝(每次行程可能不同),并且每经过一个城市时(包括 uuu 和 vvv),假如那个城市中售卖的珠宝比你现在手上的每一种珠宝都要优秀(价值更高,即严格大于),那么你就会选择购入。

现在你想要对每一次行程,求出会进行多少次购买事件。

倍增

对每个位置倍增求出祖先中第一个大于自己的,然后可以建出新的树(其实是森林如果你当0号点不存在的话)。
每次询问倍增求出u的祖先中第一个大于自己的位置(注意判断这个是否在v以下),然后再在新树上倍增。

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
int h[maxn],go[maxn*2],nxt[maxn*2],a[maxn];
int h2[maxn],g2[maxn*2],n2[maxn*2];
int f[maxn][25],g[maxn][25],dep[maxn],dp[maxn],fa[maxn][25],zjy[maxn],d[maxn];
int i,j,k,l,t,n,m,tot,top,ans;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){   
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void add(int x,int y){
    go[++tot]=y;
    nxt[tot]=h[x];
    h[x]=tot;
}
void add2(int x,int y){
    d[y]++;
    g2[++tot]=y;
    n2[tot]=h2[x];
    h2[x]=tot;
}
void dfs(int x,int y){
    dep[x]=dep[y]+1;
    f[x][0]=y;
    g[x][0]=a[x];
    int t=h[x];
    while (t){
        if (go[t]!=y) dfs(go[t],x);
        t=nxt[t];
    }
}
void dg(int x){
    int t=h2[x];
    while (t){
        fa[g2[t]][0]=x;
        dp[g2[t]]=dp[x]+1;
        dg(g2[t]);
        t=n2[t];
    }
}
int main(){
    //freopen("B.in","r",stdin);
    n=read();m=read();
    fo(i,1,n) a[i]=read();
    fo(i,1,n-1){
        j=read();k=read();
        add(j,k);add(k,j);
    }
    dfs(1,0);
    fo(i,1,n) zjy[i]=floor(log(i)/log(2));
    fo(j,1,zjy[n]) 
        fo(i,1,n){
            f[i][j]=f[f[i][j-1]][j-1];
            g[i][j]=max(g[i][j-1],g[f[i][j-1]][j-1]);
        }
    tot=0;
    fo(i,2,n){
        t=f[i][0];
        j=zjy[dep[t]];
        k=0;
        while (j>=0){
            if (g[t][j]<=a[i])  t=f[t][j];
            j--;
        }
        if (a[t]>a[i]) add2(t,i);
    }
    fo(i,1,n)
        if (!d[i]){
            dp[i]=1;
            dg(i);
        }
    fo(j,1,zjy[n])
        fo(i,1,n)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    while (m--){
        t=read();k=read();l=read();
        j=zjy[dep[t]];
        while (j>=0){
            if (g[t][j]<=l) t=f[t][j];
            j--;
        }
        if (a[t]<=l||dep[t]<dep[k]) printf("0\n");
        else{
            ans=dp[t];
            j=zjy[dp[t]];
            while (j>=0){
                if (dep[fa[t][j]]>=dep[k]) t=fa[t][j];
                j--;
            }
            ans-=dp[t];
            ans++;
            printf("%d\n",ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值