Problem A 快递

根节点为$1$ , 含有$n$个节点的树,每一条边都有一段开放的时间$[s_i,e_i]$,和经过需要的时间。

有$q$组询问,每一次在时刻$t_i$出发从根节点出发走到第$u$个节点,沿着最短路走,询问是否可行。

注意到,当在边开放的时间走到这条边上均被认为合法,走出边的时间不会因此而限制。

对于$100\%$的数据,满足$1 \leq n,q \leq 5\times 10^5$

Solution : 

  考虑能否到达$u$的条件,就是能否在$u$上方的那条边$(fa,u)$的规定时间内到达该点的父亲$fa$。

  我们注意到从根节点出发的时间为$time$,那么到达一个节点$u$的总时间就是这个点父亲前缀边的长度之和$pre$。

  转化一下,能否到达这个点上方的那一条边的条件就是$time_u + pre \in [s_{Edge} , t_{Edge}]$ 

  解出来就是$time_u \in [s_{Edge}-pre,t_{Edge}-pre]$

  所以到达一个点既有当前点的限制,又有该点到根之间点的限制,所以我们只需要将这个点到根的所有点限制的区间交起来就可以了。

  这样子,我们直接做一遍$dfs$即可,复杂度就是$O(n+q)$

# pragma GCC optimize(3)
# include <bits/stdc++.h>
# define inf (1e12)
# define int long long
# define Rint register int
# define YES putchar('Y'),putchar('E'),putchar('S')
# define NO putchar('N'),putchar('O')
using namespace std;
const int N = 5e5+10;
struct A { int pre,to,w,s,t; }a[N<<1];
struct B { int l,r; }tim[N];
int tot,n,m;
int head[N],d[N],f[N];
inline int read()
{
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
void adde(Rint u,Rint v,int w,int s,int t)
{
    a[++tot].pre=head[u];
    a[tot].to=v; a[tot].w=w;
    a[tot].s=s; a[tot].t=t;
    head[u]=tot;
}
void dfs(Rint u,Rint fa)
{
    for (Rint i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        tim[v].l=max(tim[u].l,a[i].s-d[u]);
        tim[v].r=min(tim[u].r,a[i].t-d[u]);
        d[v]=d[u]+a[i].w;
        dfs(v,u);
    }
}
signed main()
{
    n=read();m=read();
    for (Rint i=2;i<=n;i++) {
        int u=read()+1,v=read()+1,w=read(),s=read(),t=read();
        adde(u,v,w,s,t); adde(v,u,w,s,t);
    }
    tim[1].l=-inf; tim[1].r=inf;
    dfs(1,0);
    while (m--) {
        int u=read()+1,t=read();
        (t>=tim[u].l && t<=tim[u].r)?(YES):(NO);
        putchar('\n');
    }
    return 0;
}
A.cpp

相关文章: