CodeForces - 276E Little Girl and Problem on Trees(线段树)

题意
现在给你一棵这样的树,除了根节点外,其他的所有节点度都为2,也就是说除了根节点之外其他的节点都只能有一个节点,现在有两种操作:
1。以当前节点为中心,距离为d的范围内的点全部都加上x。
2。查询某个点的值
思路*

在这里插入图片描述
观察这样一张图,你会发现他们其实都是一条一条的链状的,所以对于更新来说我们可以这样,我们建两颗线段树,第一颗线段树中我们就是维护每个链上的值,每次更新的时候我们就以节点V为中心,向上或向下延申D的距离,这些区间我们加上x,那么会有两种情况,第一种就是我们向上延申的时候超过根节点我们怎么办?这样我们就建第二颗线段树,他维护的就是当我们向上延申超过根节点的时候我们直接在线段树的层上打标记,那么对于每次查询我们就查这个点本身的权值已经他所在的层数的值,第二种就是向下更新超过了他的叶子节点我们怎么办, 我们记录下来每个节点下面的儿子几点的数量然后每次更新的时候取个min就好了具体上代码,,代码写的很恶心。。。
代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 1e5 + 10;
vector<int>V[maxn];
int in[maxn] , de[maxn] , son[maxn];
int tot,n,q;
void dfs(int u, int fa,int dept)
{
    in[u] = ++tot; //DFS序 
    de[u] = dept ; // 深度 
    son[u] = 0; // 当前节点的儿子节点的个数 
    for(int i = 0 ; i < V[u].size() ; i++)
    {
        int v = V[u][i];
        if(v == fa) continue;
        dfs(v,u,dept+1);
        son[u] = son[v] + 1;
    }
}
struct seg
{
    int tree[maxn<<2];
    int add[maxn<<2];
    void pushup(int rt)
    {
        tree[rt] = tree[rt<<1] + tree[rt<<1|1];
    }
    void pushdown(int L,int R, int rt)
    {
    	
        if(add[rt] == 0) return ;
		int m = (L+R)>>1;
        add[rt<<1] += add[rt];
		add[rt<<1|1] += add[rt];
        tree[rt<<1] += add[rt] * (m-L+1);
        tree[rt<<1|1] += add[rt] * (R-m);
        add[rt] = 0;
    }
    void build(int l,int r,int rt)
    {
        tree[rt] = add[rt] = 0;
        if(l == r) return ;
        int m = (r+l)>>1;
        build(lson);
        build(rson); 
    }
    void update(int L,int R,int val,int l,int r,int rt)
    {
        if(L <= l && R >= r)
        {
            tree[rt] += val * (r-l+1);
            add[rt] += val;
            return ;
        }
        pushdown(l,r,rt);
        int m = (r+l)>>1;
        if(L <= m) update(L,R,val,lson);
        if(R > m) update(L,R,val,rson);
        pushup(rt);
    }
    int query(int pos,int l,int r,int rt)
    {
        if(l == r) return tree[rt];
        pushdown(l,r,rt);
        int m = (r+l)>>1;
        if(pos <= m) return query(pos,lson);
        else return query(pos,rson);
    }
    void print(int l,int r,int rt)
    {
        if(l == r) return ;
        pushdown(l,r,rt);
        int m = (l+r)>>1;
        print(lson);
        print(rson);
    }
}Leve,List;
void update(int v,int x,int d)
{
    if(v == 1) // 如果我们直接更新的是1号节点,那么其实我们直接对于层数打标记就好了,注意一下根据样例可以看出他是d+1 不是d 
    {
        Leve.update(1,d+1,x,1,n,1);
        return ;
    }
    if(de[v] >= d) // 如果当前深度大于我们d,就说明我们不需要对层打标记,直接在链上打标记就行 
    {
        int L = in[v] - d; // 那么我们更新的区间就是他们DFS序-d 
        int R = (son[v] < d) ? (in[v] + son[v]) : (in[v] + d);  // R就是 看看他向下可以更新的取min 
        if(de[v] == d)
        {
        	List.update(1,1,x,1,n,1);
            List.update(L+1,R,x,1,n,1);
		}
        else List.update(L,R,x,1,n,1);
    }
    else 
    {
        int len = d - de[v]; // 我们先找到需要向上更新的层数 
        Leve.update(1,len+1,x,1,n,1); // 把层数更新一下 
        int L = in[v] - (de[v] - len) + 1; // 那么我们在链上的更新其实就是他的层数更新的下一个节点 
        int R = (son[v] < d) ? (in[v] + son[v]) : (in[v] + d);
        List.update(L,R,x,1,n,1);
    }
}
int query(int v)
{
    return Leve.query(de[v]+1,1,n,1) + List.query(in[v],1,n,1);
}
signed main()
{
    
    tot = 0;
    scanf("%lld%lld",&n,&q);
    Leve.build(1,n,1);
    List.build(1,n,1);
    for(int i = 1 ; i <= n ; i++) V[i].clear();
    for(int i = 0 ; i < n - 1 ; i++)
    {
        int a,b;scanf("%lld%lld",&a,&b);
        V[a].push_back(b);
        V[b].push_back(a);
    }
    dfs(1,-1 , 0);
    int op;
    while(q--)
    {
        scanf("%lld",&op);
        if(op == 0)
        {
            int v,x,d;scanf("%lld%lld%lld",&v,&x,&d);
            update(v,x,d);
        }
        else if(op == 1)
        {
            int v;scanf("%lld",&v);
            printf("%lld\n",query(v));
        }
    }
}

猜你喜欢

转载自blog.csdn.net/wjmwsgj/article/details/82837917