libreoj 6192 「美團 CodeM 複賽」城市網絡 題解

博客觀賞效果更佳
github
cnblogs

題意簡述

nn 個城市,組成一張樹形網絡。第 ii 個城市售賣價值爲 aia_i 的珠寶。zps 的父母計劃了 qq 次行程。每次先帶上價值爲 cc 的珠寶,從城市 uu 走到城市 vv (保證 vvuu11 的路徑上)。如果當前的城市售賣的珠寶比手頭的貴(嚴格大於,等於不行),那麼 zps 的父母會買入這個珠寶。

對於每次行程,求出 zps 的父母進行了多少次“買入”操作。

n,q,c,ai105n,q,c,a_i\le 10^51u,vn1\le u,v\le n

思路

我們發現,對於每一次行程,除了第一次要特判一下之外,我下一次走到哪裏應該是固定的。

假設我們當前在 uu,那麼我們下一次走的位置,應該就是 uu 往上跳,第一個 ai>aua_i>a_uii(當然,如果這個 ii 跳到了 vv 外面,那麼我們不算它)

於是我們考慮,先用倍增求出第一個 ai>aua_i>a_uii,然後重新建圖,把 uu 直接連到 ii 上。這樣,對於每次行程,我們把首尾特判掉之後,就相當於新樹上求一段路徑的長度了。這個直接維護一個深度就能求了。

代碼

#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
    #define N 155555
    #define F(i,l,r) for(int i=l;i<=r;++i)
    #define D(i,r,l) for(int i=r;i>=l;--i)
    #define Fs(i,l,r,c) for(int i=l;i<=r;c)
    #define Ds(i,r,l,c) for(int i=r;i>=l;c)
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)
    #define Tra(i,u) for(int i=G.Start(u),v=G.To(i);~i;i=G.Next(i),v=G.To(i))
    #define p_b push_back
    #define sz(a) ((int)a.size())
    #define iter(a,p) (a.begin()+p)
    int I()
    {
        int x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return (x=(f==1)?x:-x);
    }
    void Rd(int cnt,...)
    {
        va_list args; va_start(args,cnt);
        F(i,1,cnt) {int* x=va_arg(args,int*);(*x)=I();}
        va_end(args);
    }
    class Graph
    {
        public:
            int head[N];
            int EdgeCount;
            struct Edge
            {
                int To,Label,Next;
            }Ed[N<<1];
            void clear(int _V=N,int _E=N<<1) 
            {
                memset(Ed,-1,sizeof(Edge)*(_E));
                memset(head,-1,sizeof(int)*(_V));
                EdgeCount=-1;
            }
            void AddEdge(int u,int v,int w=1)
            {
                Ed[++EdgeCount]=(Edge){v,w,head[u]};
                head[u]=EdgeCount;
            }
            void Add2(int u,int v,int w=1) {AddEdge(u,v,w);AddEdge(v,u,w);}
            int Start(int u) {return head[u];}
            int To(int u){return Ed[u].To;}
            int Label(int u){return Ed[u].Label;}
            int Next(int u){return Ed[u].Next;}
    }G;
    
    int n,m;
    int a[N];
    void Input()
    {
        Rd(2,&n,&m);
        F(i,1,n) a[i]=I();
        G.clear();
        F(i,1,n-1)
        {
            int u,v; Rd(2,&u,&v); G.Add2(u,v);
        }
    }

    int deep[N],fa[N][22],Max[N][22];
    void DFS(int u,int f)
    {
        deep[u]=(u==f)?0:deep[f]+1;
        fa[u][0]=f; F(i,1,20) fa[u][i]=fa[fa[u][i-1]][i-1];
        Max[u][0]=max(a[u],a[f]); F(i,1,20) Max[u][i]=max(Max[u][i-1],Max[fa[u][i-1]][i-1]);
        Tra(i,u) if (v!=f) DFS(v,u);
    }
    int PathMax(int u,int f) // 求路徑最大值
    {
        if (u==f) return a[u];
        int ans=0;
        D(i,20,0) if (deep[fa[u][i]]>=deep[f]) 
        {
            ans=max(ans,Max[u][i]),u=fa[u][i];
        }
        
        return ans;
    }
    int MaxPos(int u,int f) // 求路徑最大值是哪個位置
    {
        int Mx=PathMax(u,f);
        if (a[u]==Mx) return u;
        D(i,20,0) if (Max[u][i]<Mx) u=fa[u][i]; 
        return fa[u][0];
    }
    int FirstBig(int u,int c) // 找到 u 往上第一個滿足 a[i]>c 的
    {
        if (a[u]>c) return u;
        if (Max[u][20]<=c) return n+1;
        D(i,20,0) if (Max[u][i]<=c) u=fa[u][i];
        return fa[u][0];
    }
    namespace Re_build // 把重新建的圖封裝起來,避免名字衝突
    {   
        Graph G;
        int deep[N],fa[N][22];
        void Init()
        {
            G.clear();
            F(i,1,n) G.AddEdge(fa[i][0],i); 
        }
        void DFS(int u)
        {
            deep[u]=(u==n+1)?0:deep[fa[u][0]]+1;
            F(i,1,20) fa[u][i]=fa[fa[u][i-1]][i-1];
            Tra(i,u) DFS(v);
        }
        int  Query(int u,int fu) {return deep[u]-deep[fu];}
    }
    void Soviet()
    {
        DFS(1,1);
        F(i,1,n) Re_build::fa[i][0]=(Max[i][20]==a[i])?n+1:FirstBig(i,a[i]);
        // 重新設置 fa 數組
        Re_build::Init();
        Re_build::DFS(n+1);
        // 重新 DFS 一遍

        F(i,1,m)
        {
            int u,v,c; Rd(3,&u,&v,&c); 
            int uu=u,vv=v; // 存儲原始的 u,v (後面會有修改)
            if (u==v) {puts(c<a[u]?"1":"0"); continue;}
            if (c>=PathMax(u,v)) {puts("0"); continue;}
            u=FirstBig(uu,c);  // 先跳到上面第一個 >c 的 (注意,這裏先跳了一次,所以答案+1)
            v=MaxPos(uu,vv); // 處理一下開頭結尾
            printf("%d\n",Re_build::Query(u,v)+1); 
            // 如上面所說,答案 +1
        }
    }

    #define Flan void
    Flan IsMyWife()
    {
        Input();
        Soviet();
    }
}
int main()
{
    Flandre_Scarlet::IsMyWife();
    getchar();getchar();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章