Codeforces 1110F. Nearest Leaf (线段树+换根+离线处理)

题目连接:https://codeforces.com/contest/1110/problem/F 

F. Nearest Leaf

time limit per test

4 seconds

memory limit per test

512 megabytes

input

standard input

output

standard output

Let's define the Eulerian traversal of a tree (a connected undirected graph without cycles) as follows: consider a depth-first search algorithm which traverses vertices of the tree and enumerates them in the order of visiting (only the first visit of each vertex counts). This function starts from the vertex number 11 and then recursively runs from all vertices which are connected with an edge with the current vertex and are not yet visited in increasing numbers order. Formally, you can describe this function using the following pseudocode:

next_id = 1
id = array of length n filled with -1
visited = array of length n filled with false

function dfs(v):
    visited[v] = true
    id[v] = next_id
    next_id += 1
    for to in neighbors of v in increasing order:
        if not visited[to]:
            dfs(to)

You are given a weighted tree, the vertices of which were enumerated with integers from 11 to nn using the algorithm described above.

A leaf is a vertex of the tree which is connected with only one other vertex. In the tree given to you, the vertex 11 is not a leaf. The distance between two vertices in the tree is the sum of weights of the edges on the simple path between them.

You have to answer qq queries of the following type: given integers vv, ll and rr, find the shortest distance from vertex vv to one of the leaves with indices from ll to rr inclusive.

Input

The first line contains two integers nn and qq (3≤n≤500000,1≤q≤5000003≤n≤500000,1≤q≤500000) — the number of vertices in the tree and the number of queries, respectively.

The (i−1)(i−1)-th of the following n−1n−1 lines contains two integers pipi and wiwi (1≤pi<i,1≤wi≤1091≤pi<i,1≤wi≤109), denoting an edge between vertices pipi and ii with the weight wiwi.

It's guaranteed that the given edges form a tree and the vertices are enumerated in the Eulerian traversal order and that the vertex with index 11 is not a leaf.

The next qq lines describe the queries. Each of them contains three integers vivi, lili, riri (1≤vi≤n,1≤li≤ri≤n1≤vi≤n,1≤li≤ri≤n), describing the parameters of the query. It is guaranteed that there is at least one leaf with index xx such that li≤x≤rili≤x≤ri.

Output

Output qq integers — the answers for the queries in the order they are given in the input.

Examples

input

Copy

5 3
1 10
1 1
3 2
3 3
1 1 5
5 4 5
4 1 2

output

Copy

3
0
13

input

Copy

5 3
1 1000000000
2 1000000000
1 1000000000
1 1000000000
3 4 5
2 1 5
2 4 5

output

Copy

3000000000
1000000000
2000000000

input

Copy

11 8
1 7
2 1
1 20
1 2
5 6
6 2
6 3
5 1
9 10
9 11
5 1 11
1 1 4
9 4 8
6 1 4
9 7 11
9 10 11
8 1 11
11 4 5

output

Copy

8
8
9
16
9
10
0
34

Note

In the first example, the tree looks like this:

In the first query, the nearest leaf for the vertex 11 is vertex 44 with distance 33. In the second query, the nearest leaf for vertex 55 is vertex 55with distance 00. In the third query the nearest leaf for vertex 44 is vertex 44; however, it is not inside interval [1,2][1,2] of the query. The only leaf in interval [1,2][1,2] is vertex 22 with distance 1313 from vertex 44.

 

题目大意是给出一颗带有权值的树,然后让你按dfs序(即欧拉序)的顺序对节点标号,上面的伪代码很容易可以看出是dfs序的伪代码,然后会有q组查询,x,l,r,询问标号l到r区间内的叶子节点到x节点的最短距离

 

首先我们来考虑,查询x如果为根(起始我们先默认根节点是1),那么查询的就是l到r区间叶子节点到根节点的最小距离即可,对于查询区间,我们可以考虑用线段树来保存任意一个叶子节点到根节点的最小距离(注意不是叶子节点的可以直接赋值最大值),然后进行线段树区间查询最小值即可

然后考虑x不为根,我们可以考虑把根提起来,例如下图,起始跟为1,然后我们把3节点作为根,提出来,会发现,对于3这颗子树下的叶子节点的距离,减去1到3之间的距离就是把3换为这颗树的根的距离,不是3的子树下的叶子节点,加上1到3之间的距离也作为3位根的时候最短距离,刚刚我们把非叶子节点赋值最大值是因为一个树无论怎么换根,不可能会把非叶子节点变成叶子节点(可能叶子节点会变成根,但是不影响)

最后就是处理方式了,考虑离线处理,先把查询的x节点全部都保存下来,然后去dfs遍历一遍这棵树,遍历到x节点,我们就考虑查询x节点的ans,怎么查询呢,要利用上面的结论,非根节点的子树叶子节点加上该节点到原根节点的距离,根节点下的子树叶子节点减去该节点到原根节点的距离,进行线段树区间更新,对于每次查询给出ans,最后还要还原这颗线段树,因为这棵树还需要继续遍历,找其他节点x的ans

代码如下:

#include <bits/stdc++.h>
#include <time.h>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

typedef long long ll;
typedef pair<ll,ll>  P;
const int maxn = 1e6 + 5000;
const ll mod = 1e9 + 7;
ll c,k;
vector<vector<P> >v;
int tot = 0;
int vis[maxn],l[maxn],r[maxn],n,q;
ll dis[maxn];
void dfs(int x,int fa,ll cur) {
    l[x] = ++tot;
    if(v[x].size()) dis[x] =INF;
    else dis[x] = cur;
    for(auto d:v[x]) {
        if(d.first != fa) {
            dfs(d.first,x,cur + d.second);
        }
    }
    r[x] = tot;
}

struct node {
    ll Min;
    ll lazy;
} no[maxn<<2];
void pushup(int id) {
    no[id].Min=min(no[id<<1].Min,no[id<<1|1].Min);
}
void pushdown(int id) {
    no[id<<1].lazy+=no[id].lazy;
    no[id<<1|1].lazy+=no[id].lazy;
    no[id<<1].Min+=no[id].lazy;
    no[id<<1|1].Min+=no[id].lazy;
    no[id].lazy=0;
}
void build(int id,int l,int r) {
    no[id].lazy=0;
    if(l==r) {
        no[id].Min=dis[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    pushup(id);
}
void update(int id,int l,int r,int L,int R,ll w) {
    if(l>=L && r<=R) {
        no[id].lazy+=w;
        no[id].Min+=w;
        return ;
    }
    if(no[id].lazy)
        pushdown(id);
    int mid=(l+r)>>1;
    if(L<=mid)
        update(id<<1,l,mid,L,R,w);
    if(R>mid)
        update(id<<1|1,mid+1,r,L,R,w);
    pushup(id);
}
ll query(int id,int l,int r,int L,int R) {
    if(l>=L && r<=R)
        return no[id].Min;
    if(no[id].lazy)
        pushdown(id);
    int mid=(l+r)>>1;
    ll ans=INF;
    if(L<=mid)
        ans=query(id<<1,l,mid,L,R);
    if(R>mid)
        ans=min(ans,query(id<<1|1,mid+1,r,L,R));
    return ans;
}
struct queryans {
    int l,r,id;
    ll ans;
    queryans() {}
    queryans(int lll,int rr,int idd,ll ans) {
        l = lll,r = rr,id = idd,ans = 0;
    }
};
vector<queryans>ans[maxn];
ll anss[maxn];
void dfss(int x,ll w) {
    update(1,1,n,1,n,w);
    update(1,1,n,l[x],r[x],-2ll * w);
    for(auto d:ans[x])
      anss[d.id] = query(1,1,n,d.l,d.r);
    for(auto d:v[x]) {
        ll w = d.second;
        int y = d.first;
        dfss(y,w);
    }
    update(1,1,n,1,n,-w);
    update(1,1,n,l[x],r[x],2ll * w);
}


int main() {
    ios::sync_with_stdio(false);
    while(cin >> n >> q) {
        tot = 0;
        v.resize(n + 200);
        memset(vis,0,sizeof(vis));
        memset(dis,0,sizeof(dis));
        for(int i = 2; i <= n; i++) {
            int u,vv;
            cin >> u >> vv;
            v[u].push_back(make_pair(i,vv));
        }
        dfs(1,0,0);
        build(1,1,n);
//        cout << query(1,1,n,1,n) << endl;
        for(int i = 1; i <= q; i++) {
            ll x,ll,rr;
            cin >> x >> ll >> rr;
            ans[x].push_back(queryans(ll,rr,i,0));
        }
        dfss(1,0);
        for(int i = 1;i <= q;i++) cout << anss[i] << endl;
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值