CodeForces 1110F Nearest Leaf(离线处理 + 线段树)

CodeForces 1110F Nearest Leaf(离线处理 + 线段树)

CodeForces 1110F Nearest Leaf(离线处理 + 线段树)

CodeForces 1110F Nearest Leaf(离线处理 + 线段树)

 

 

大致题意:给你一棵n个节点的带有边权的树,给你q个询问。每个询问给出一个点x和一个区间[l,r],问在dfs序在这个区间的点中,距离x最近的叶子节点距离x的距离是多少。

首先,我们考虑,如果每个询问的点x都是根,那么我们每次寻找一个距离最近的叶子节点的距离,相当于在这个区间中找叶子节点的最小值。因此,很自然而然的,我们可以把根距离每个叶子节点的距离放到线段树里面,那么问题就变成了求一个区间最小值。

其次,我们来考虑如果不是根节点则么办。同样的,我们还是可以考虑把询问的点提起来,把它变成根节点。我们考虑同样用dfs的顺序依次序换根。以样例为例子,原本的根为1,当根变成3之后,3子树外面的所有点的距离都会增加1,而子树里面的点的距离会减少1。对应的,可以通过先整体区间减去1,再把3的子树里面整体增加2来实现这一过程。这么变化之后,我们的根就变成了3。依照顺序,我们可以尝试把每一个节点变成根,对于每个询问,当它的询问点成为根的时候,就可以直接通过区间最小值求得当前询问的答案。

具体来说,我们通过邻接表的方式,把询问也离线的存储在每个点上,然后每次把这个点变为根的时候就解决它。最后按顺序输出即可。关于这种换根的方法,之前我们也遇到过了,今天再次见到了。具体见代码:

#include <bits/stdc++.h>
#define INF 1e18
#define LL long long
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;

const int N = 500010;
const int mod = 1e9 + 7;

LL ans[N],d[N];
int v[N],w[N],n,q,e=1,Q;
int nxt[N],g[N],Ls[N];
int nex[N],l[N],r[N],la[N];

struct ST
{
    #define ls i<<1
    #define rs i<<1|1

	struct node
	{
	    LL min,lazy;
	    int l,r;
    } T[N<<2];

	void build(int i,int l,int r)
	{
		T[i]=node{0,0,l,r};
		if (l==r)
		{
			T[i].min=d[l];
			return;
		}
		int mid=(l+r)>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		T[i].min=min(T[ls].min,T[rs].min);
	}

	void push_down(int i)
	{
		T[ls].lazy+=T[i].lazy;
		T[rs].lazy+=T[i].lazy;
		T[ls].min+=T[i].lazy;
		T[rs].min+=T[i].lazy;
		T[i].lazy=0;
	}

	void update(int i,int l,int r,LL x)
	{
		if ((T[i].l==l)&&(T[i].r==r))
		{
			T[i].lazy+=x;
			T[i].min+=x; return;
		}
		if (T[i].lazy!=0) push_down(i);
		int mid=(T[i].l+T[i].r)>>1;
		if (mid>=r) update(ls,l,r,x);
		else if (mid<l) update(rs,l,r,x);
		else
		{
			update(ls,l,mid,x);
			update(rs,mid+1,r,x);
		}
		T[i].min=min(T[ls].min,T[rs].min);
	}

	LL getmin(int i,int l,int r)
	{
		if ((T[i].l==l)&&(T[i].r==r)) return T[i].min;
		if (T[i].lazy!=0) push_down(i);
		int mid=(T[i].l+T[i].r)>>1;
		if (mid>=r) return getmin(ls,l,r);
		else if (mid<l) return getmin(rs,l,r);
		else return min(getmin(ls,l,mid),getmin(rs,mid+1,r));
	}

} seg;

inline void addedge(int x,int y)
{
    g[++e]=y; nxt[e]=Ls[x]; Ls[x]=e;
}

inline void addquery(int x,int L,int R)
{
    l[++Q]=L; r[Q]=R;
    nex[Q]=la[x]; la[x]=Q;
}

inline void update(int x,int d)
{
    seg.update(1,1,n,d);
    seg.update(1,x,v[x],-(d<<1));
}

void dfs(int x)
{
    update(x,w[x]);
    for(int i=la[x];i;i=nex[i])
        ans[i]=seg.getmin(1,l[i],r[i]);
    for(int i=Ls[x];i;i=nxt[i]) dfs(g[i]);
    update(x,-w[x]);
}

int main()
{
    scc(n,q);
    for(int i=2;i<=n;i++)
    {
        int y=i,x; scc(x,w[i]);
        d[y]=d[x]+w[i]; addedge(x,y);
    }
    for(int i=n;i;i--)
        if (Ls[i]) v[i]=v[Ls[i]],d[i]=INF;
                            else v[i]=i;
    for(int i=1;i<=q;i++)
    {
        int x,l,r;
        sccc(x,l,r);
        addquery(x,l,r);
    }
    seg.build(1,1,n);
    dfs(1);
    for(int i=1;i<=q;i++)
        printf("%lld\n",ans[i]);
    return 0;
}

 

相关文章:

  • 2021-10-09
  • 2021-10-07
  • 2022-12-23
  • 2022-01-01
  • 2021-11-23
  • 2022-01-14
  • 2021-08-07
  • 2021-10-30
猜你喜欢
  • 2022-03-03
  • 2021-07-03
  • 2022-12-23
  • 2021-05-06
  • 2021-06-28
  • 2022-12-23
  • 2021-09-14
相关资源
相似解决方案