CF983E NN country

题意

给定一棵树和若干条路线,每条路线相当于 $a,b$ 之间的路径,途径路径上的每个点。

给出若干个询问,每次询问从 $x$ 到 $y$ 至少需要利用几条路线。

Solution

考虑子问题,两个询问点在同一条链上,这样问题就类似 [SCOI2015] 国旗计划,只不过这道题是环上问题,但思路相同。

对于链,考虑贪心,对于每一个点 $fr_i$ 跳到能到达的最远的点 $to_i$,容易想到下一步应当是跳到 $to_{to_i}$,故考虑倍增优化这个不断往前跳的过程。定义 $jump_{i,k}$ 为点 $i$ 跳 $2^k$ 步能到达的最远节点,可以用 $\mathcal{O}(n\log n)$ 复杂度的时间来处理出 $jump$ 数组。

考虑树上的两个点,对于 $x$ 是 $y$ 的祖先节点($y$ 为 $x$ 祖先节点时同理)的情况,同链上情况处理。

对于两个点分别不是对方父亲节点的情况,考虑将问题拆分为 $x$ 到 $lca$ 和 $y$ 到 $lca$ 两个问题处理。令 $ans_x$ 为 $x$ 跳到 $lca$ 的最小步数,$ans_y$ 为 $y$ 跳到 $lca$ 的最小步数,$pre_x$ 为 $x$ 向上跳 $ans_x-1$ 步到达的深度最浅的节点,即跳到 $lca$ 的前一个节点,$pre_y$ 同理。考虑两种情况:

  • 有一条路线同时经过 $pre_x$ 和 $pre_y$;
  • 不存在一条路线同时经过 $pre_x$ 和 $pre_y$。

对于第二种情况,答案即为 $ans_x+ans_y$,对于第一种情况,答案为 $ans_x+ans_y-1$。问题转化为如何维护是否存在一条路线经过两个点。

发现对于一个节点 $u$,只要 $u$ 的子树中存在一个点,使得存在一条从其出发的路径在 $v$ 的子树中结束,则存在一条路径同时经过 $u$ 和 $v$。考虑通过 dfs 序转化为区间问题,令 $size_i$ 为节点 $i$ 的子树大小,则问题进一步转化为询问是否存在一条路径 $(fr,to)$ 使得 $dfn_{fr}\in[dfn_x,dfn_x+size_x-1]$ ,$dfn_{to}\in[dfn_y,dfn_y+size_y-1]$。考虑二维数点,即查询平面上矩形 $[(dfn_x,dfn_y),(dfn_x+size_x-1,dfn_y+size_y-1)]$ 是否有点。将询问离线排序并用树状数组维护即可。

有个小细节:由于 $(fr,to)$ 和 $(to,fr)$ 在此题中是等价的,故在插入点时都应插入,否则可能会统计不到这个点。

其他具体实现细节见代码,以及由于我不会倍增求 $lca$,所以写了个树剖。

总时间复杂度应该是 $\mathcal{O}(n\log n)$ 的,但是有巨大常数,可以通过本题。

code

#include<bits/stdc++.h>
inline int read()
{
    int res=0,flag=1;
    char ch=getchar();
    while(!isalnum(ch)) (ch=='-')?flag=-1:1,ch=getchar();
    while(isalnum(ch)) res=res*10+ch-'0',ch=getchar();
    return res*flag;
}
struct edge 
{
    int to,nxt;
};
int n,m,q,tot=0;
int head[200010];
struct edge ed[400010];
void add_edge(int fr,int to)
{
    ed[++tot]=(edge){to,head[fr]};
    head[fr]=tot;
    return ;
}
namespace TreePartition
{
    struct node
    {
        int fa,son;
        int top;
        int size;
        int dep;
    };
    const int root=1;
    struct node nd[200010];
    void set(int fa,int fr)
    {
        nd[fr].size=1;
        nd[fr].fa=fa;
        nd[fr].dep=nd[fa].dep+1;
        for(int i=head[fr];i!=0;i=ed[i].nxt)
        {
            int to=ed[i].to;
            if(to==fa)
                continue;
            set(fr,to);
            nd[fr].size+=nd[to].size;
            if(nd[to].size>nd[nd[fr].son].size)
                nd[fr].son=to;
        }
        return ;
    }
    void dfs(int fa,int fr)
    {
        if(nd[fr].son!=0)
        {
            int to=nd[fr].son;
            nd[to].top=nd[fr].top;
            dfs(fr,to);
        }
        for(int i=head[fr];i!=0;i=ed[i].nxt)
        {
            int to=ed[i].to;
            if(to==fa||to==nd[fr].son)
                continue;
            nd[to].top=to;
            dfs(fr,to);
        }
        return ;
    }
    void init()
    {
        nd[root].dep=1;
        nd[root].top=root;
        set(0,root);
        dfs(0,root);
        return ;
    }
    int lca(int x,int y)
    {
        int fx=nd[x].top;
        int fy=nd[y].top;
        while(fx!=fy)
        {
            if(nd[fx].dep<nd[fy].dep)
                std::swap(x,y),std::swap(fx,fy);
            if(x==root)
                return root;
            x=nd[fx].fa;
            fx=nd[nd[fx].fa].top;
        }
        if(nd[x].dep>nd[y].dep)
            std::swap(x,y);
        return x;
    }
}
namespace BIT
{
    struct point
    {
        int x,y;
        bool operator <(const struct point &other)const
        {
            if(this->x==other.x)
                return this->y<other.y;
            return this->x<other.x; 
        }
    };
    struct question
    {
        int id;
        int val;
        int x,y;
        bool operator <(const struct question &other)const
        {
            if(this->x==other.x)
                return this->y<other.y;
            return this->x<other.x; 
        }
    };
    int pts,cnt;
    struct point points[800010];
    struct question questions[1600010];
    int data[400010];
    int lowbit(int x)
    {
        return x&(-x);
    }
    void modify(int pos,int val)
    {
        if(pos==0)
            return ;
        for(int i=pos;i<=n;i+=lowbit(i))
            data[i]+=val;
        return ;
    }
    int query(int pos)
    {
        int res=0;
        for(int i=pos;i>=1;i-=lowbit(i))
            res+=data[i];
        return res;
    }
};
namespace solve
{
    int dfncnt;
    int ans[200010],tmp[200010];
    int dfn[200010];
    int jump[200010][21];
    std::vector<int> graph[200010];
    void dfs(int fa,int fr)
    {
        dfn[fr]=++dfncnt;
        for(int i=head[fr];i!=0;i=ed[i].nxt)
        {
            int to=ed[i].to;
            if(to==fa)
                continue;
            dfs(fr,to);
        }
        return ;
    } 
    void set(int fa,int fr) 
    {
        using namespace TreePartition;
        jump[fr][0]=fr;
        for(auto Lca:graph[fr])
            if(nd[jump[fr][0]].dep>nd[Lca].dep)
                jump[fr][0]=Lca;
        for(int i=head[fr];i!=0;i=ed[i].nxt)
        {
            int to=ed[i].to;
            if(to==fa)
                continue;
            set(fr,to);
            if(nd[jump[fr][0]].dep>nd[jump[to][0]].dep)
                jump[fr][0]=jump[to][0];
        }
        return ;
    }
    void input()
    {
        n=read();
        for(int to=2;to<=n;to++)
        {
            int fr=read();
            add_edge(fr,to);
            add_edge(to,fr);
        }
        TreePartition::init();
        dfs(0,1); 
        m=read();
        for(int i=1;i<=m;i++)
        {
            int fr=read(),to=read();
            int Lca=TreePartition::lca(fr,to);
            if(fr!=Lca)
                graph[fr].push_back(Lca);
            if(to!=Lca)
                graph[to].push_back(Lca);
            using namespace BIT;
            points[++pts]=(point){dfn[fr],dfn[to]};
            points[++pts]=(point){dfn[to],dfn[fr]};
        }
        set(0,1);
        q=read();
        return ;
    } 
    void init()
    {
        for(int i=1;i<=20;i++)
            for(int j=1;j<=n;j++)
                jump[j][i]=jump[jump[j][i-1]][i-1];
        return ; 
    } 
    void solve()
    {
        for(int i=1;i<=q;i++)
        {
            using namespace TreePartition;
            int fr=read(),to=read();
            int Lca=lca(fr,to);
            if(dfn[fr]>dfn[to])
                std::swap(fr,to); 
            for(int j=20;j>=0;j--)
                if(jump[fr][j]!=0&&nd[jump[fr][j]].dep>nd[Lca].dep)
                    fr=jump[fr][j],ans[i]+=(1<<j);
            for(int j=20;j>=0;j--)
                if(jump[to][j]!=0&&nd[jump[to][j]].dep>nd[Lca].dep)
                    to=jump[to][j],ans[i]+=(1<<j);
            if(nd[jump[fr][0]].dep>nd[Lca].dep||nd[jump[to][0]].dep>nd[Lca].dep) 
            {
                ans[i]=-1;
                continue;
            }
            if(fr==Lca)
            {
                ans[i]++;
                continue;
            };
            ans[i]+=2;
            int sizefr=nd[fr].size;
            int sizeto=nd[to].size;
            using namespace BIT;
            questions[++cnt]=(question){i,1,dfn[fr]+sizefr-1,dfn[to]+sizeto-1};
            questions[++cnt]=(question){i,-1,dfn[fr]+sizefr-1,dfn[to]-1};
            questions[++cnt]=(question){i,-1,dfn[fr]-1,dfn[to]+sizeto-1};
            questions[++cnt]=(question){i,1,dfn[fr]-1,dfn[to]-1};
        }
        using namespace BIT;
        std::sort(points+1,points+pts+1);
        std::sort(questions+1,questions+cnt+1);
        for(int i=1,now=1;i<=cnt;i++)
        {
            while(now<=pts&&points[now].x<=questions[i].x)
                modify(points[now].y,1),now++;
            tmp[questions[i].id]+=questions[i].val*query(questions[i].y);
        }
        for(int i=1;i<=q;i++)
        {
            if(tmp[i]>0)
                ans[i]--;
            printf("%d\n",ans[i]);
        }
        return ;
    }
}; 
int main(int argc,const char *argv[])
{
    solve::input();
    solve::init();
    solve::solve();
    return 0;
}
posted @   Che_001  阅读(7)  评论(0编辑  收藏  举报  
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示