Codeforces 1304E 1-Trees and Queries 题解

博客观赏效果更佳

题意简述

n n n 个节点的树,每天边权都是 1 1 1。有 q q q 个询问,每次给定 x , y , a , b , k x,y,a,b,k x,y,a,b,k,表示你在树上加一条边 < x , y > <x,y> <x,y> ,并求从 a a a b b b k k k 条边的最短路。其中每条边和点都允许重复经过。求完询问后,把 < x , y > <x,y> <x,y> 这条边删掉(即:询问之间都是独立的)。

n ≤ 1 e 5 ;   q ≤ 1 e 5 ;   1 ≤ x , y , a , b ≤ n ;   1 ≤ k ≤ 1 e 9 n\le 1e5;\ q\le 1e5;\ 1\le x,y,a,b \le n;\ 1\le k \le 1e9 n1e5; q1e5; 1x,y,a,bn; 1k1e9

思路框架

首先倍增LCA维护两点之间的最短路。

由于边能重复经过,参考今年普及 T4 的思路,我们只要找一条长度 ≤ k \le k k 并且和 k k k 同奇偶的路即可。

原先 a , b a,b a,b之间的最短路只能有一条。但是加上边 x , y x,y x,y 之后,就多了两条:
a → x → y → b a\rightarrow x \rightarrow y \rightarrow b axyb,长度为 Q ( a , x ) + 1 + Q ( y , b ) Q(a,x)+1+Q(y,b) Q(a,x)+1+Q(y,b)
a → y → x → b a\rightarrow y \rightarrow x \rightarrow b ayxb,长度为 Q ( a , y ) + 1 + Q ( x , b ) Q(a,y)+1+Q(x,b) Q(a,y)+1+Q(x,b)
(其中 Q ( u , v ) Q(u,v) Q(u,v) 表示 u u u v v v 的最短路,代码里叫PathLen
这三条里面判断一下,哪条能满足:长度 ≤ k \le k k 且长度和 k k k同奇偶

有一个就输出YES,否则NO

代码

#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)
    void R1(int &x)
    {
        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();
        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*);R1(*x);
        }
        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;
    void Input()
    {
        R1(n);
        G.clear();
        F(i,1,n-1)
        {
            int u,v;Rd(2,&u,&v);
            G.Add2(u,v);
        }
    }

    int fa[N][22],deep[N];
    void DFS(int u,int f)
    {
        deep[u]=(u==f)?0:deep[f]+1; //deep[i] 表示从 i 到根经过的 **边数** 
        //所以 deep[根] 是 0 哦
        fa[u][0]=f; 
        F(i,1,20) fa[u][i]=fa[fa[u][i-1]][i-1];
        Tra(i,u)
        {int v=__v;
            if (v!=f) DFS(v,u);
        }
    }
    int LCA(int a,int b) //求a,b的LCA
    {
        if (deep[a]<deep[b]) swap(a,b);
        D(i,20,0) if (deep[fa[a][i]]>=deep[b]) a=fa[a][i];
        if (a==b) return a;
        D(i,20,0) if (fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i];
        return fa[a][0];
    }
    int PathLen(int a,int b) 
    {
        return deep[a]+deep[b]-2*deep[LCA(a,b)];
    }
    bool cxk(int path,int k){return path<=k and (k-path)%2==0;}
    void Soviet()
    { 
        DFS(1,1);
        int q;R1(q);
        F(i,1,q)
        {
            int a,b,x,y,k;
            Rd(5,&x,&y,&a,&b,&k);
            int path1=PathLen(a,b);
            int path2=PathLen(a,x)+1+PathLen(y,b);
            int path3=PathLen(a,y)+1+PathLen(x,b); //上面讨论的三条路
            if (cxk(path1,k) or cxk(path2,k) or cxk(path3,k)) puts("YES");
            else puts("NO");
        }
    }

    #define Flan void
    Flan IsMyWife()
    {
        Input();
        Soviet();
    }
}
int main()
{
    Flandre_Scarlet::IsMyWife();
    getchar();getchar();
    return 0;
}

后记: 版权问题

(不想看就算了)
考场上,我想出了这个算法(第一个)。然后我把这个算法告诉了我的好朋友lym。他又把这个算法告诉了他的好朋友zhk

最近zhk也像我一样搭了一个hexo博客,他就来找我帮他调试博客的功能。然后我发现了他有一篇文章,就是这个的题解,同步发表于洛谷博客的。我一看这思路似乎很眼熟,便去问他你是怎么想出这思路的。

(开始回溯)他说,是lym告诉他的,lym给了他一张截图。

截图:

你们看这个头像和我的是否有几分相像呢(滑稽)。这真是 缘 分 的 天 空 啊。

(备注:现在换头像了 qaq,但是有一个半边还是一样的,可以仔细观察一下)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值