Codeforces 383C. Propagating tree(树上hash映射+线段树懒操作)->(一种将树结点规律化的方法)

C. Propagating tree
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Iahub likes trees very much. Recently he discovered an interesting tree named propagating tree. The tree consists of n nodes numbered from 1 to n, each node i having an initial value ai. The root of the tree is node 1.

This tree has a special property: when a value val is added to a value of node i, the value -val is added to values of all the children of node i. Note that when you add value -val to a child of node i, you also add -(-val) to all children of the child of node i and so on. Look an example explanation to understand better how it works.

This tree supports two types of queries:

  • "1 x val" — val is added to the value of node x;
  • "2 x" — print the current value of node x.

In order to help Iahub understand the tree better, you must answer m queries of the preceding type.

Input

The first line contains two integers n and m (1 ≤ n, m ≤ 200000). The second line contains n integers a1a2, ..., an (1 ≤ ai ≤ 1000). Each of the next n–1 lines contains two integers vi and ui (1 ≤ vi, ui ≤ n), meaning that there is an edge between nodes vi and ui.

Each of the next m lines contains a query in the format described above. It is guaranteed that the following constraints hold for all queries:1 ≤ x ≤ n, 1 ≤ val ≤ 1000.

Output

For each query of type two (print the value of node x) you must print the answer to the query on a separate line. The queries must be answered in the order given in the input.

Sample test(s)
input
5 5
1 2 1 1 2
1 2
1 3
2 4
2 5
1 2 3
1 1 2
2 1
2 2
2 4
output
3
3
0
Note

The values of the nodes are [1, 2, 1, 1, 2] at the beginning.

Then value 3 is added to node 2. It propagates and value -3 is added to it's sons, node 4 and node 5. Then it cannot propagate any more. So the values of the nodes are [1, 5, 1,  - 2,  - 1].

Then value 2 is added to node 1. It propagates and value -2 is added to it's sons, node 2 and node 3. From node 2 it propagates again, adding value 2 to it's sons, node 4 and node 5. Node 3 has no sons, so it cannot propagate from there. The values of the nodes are [3, 3,  - 1, 0, 1].

You can see all the definitions about the tree at the following link: http://en.wikipedia.org/wiki/Tree_(graph_theory)


这题看了,想了下就会了.. 只不过拍出来太慢了,个人还需要大大提高代码能力


首先,题目给了一棵树,但这棵树的结点顺序不是我们希望的,

这是这个方法解决这道题最重要的地方,所以我特意做了两幅图来解释(做得不好勿喷0.0)


如上图这一棵树

我们会发现,1的儿子是2,3;2的儿子是4,6,7;16的儿子却是5

这样子,我们就不好进行操作了

我们希望对一个点加上一个val的时候,直接将下它和它的子树所有的点加上一个值,最后查询的时候,直接查询对应点的值,并用一些技巧取正负即可

那么,我们就希望待加上val值的结点和它的子树的所有结点,整体的编号是逐一递增的,例4 5 6 7 8...这样子

如果能构成一个这个样子的结构,我们在进行第一种操作时,只需从它到 它子树最远可到的点 之间的所有点加上一个val值即可

那么,这就可以用一棵线段树来做了,这样子就满足了线段树所需要的点紧密分布的条件,其更新和查询的时间复杂度是O(log)级别的

那么,我们将上面那一棵树映射成下面这一棵树:

(将结点乱而无序的一棵树  变成  结点有序有规律的一棵树)


发现,点的编号顺序改变了

根结点始终为1,这个不变

每个结点i,其子树的结点是 i,i+1,i+2,i+3,...... ,i+n,n为其子树的结点总数

例如,7的子树,就是7,8,9三个结点;14的子树是,14,15,16,17四个结点

而2的子树就是,2,3,.......,11 十个结点

我们会发现,这样子的一个结构,正好满足其子树的结点编号是类似于自然数那样紧密分布的,例4,5,6,7,8.......

那么,我们可以考虑用树上hash的办法,将原树(第一幅图)的结点映射到现在这棵树的结点,可以保证这个映射是双射(单射+满射)

同时,我们可以知道树(指的是现在这棵树,下同)每个结点其子树最远到达哪个结点

比如:7最远到达9;14最远到达17;1最远到达17,6最远达到6;

那么,当进行第一种操作的时候,就只需要在线段树上将from到to进行懒操作更新,其中from是当前结点,to是最远可达结点;这是相当快速的


接下来讲第二种操作,查询操作

有了上面的方法,查询操作就显而易见。先找到需要查询的结点x在树上对应的是哪个结点now,然后查询now的值

就可以了吗?

现在就遇到了一个问题,我们会发现,根据题意,将14加上val的时候,15和17是加上一个-val,16是加上一个val

如果子树都只是加上一个val,而没有什么正负,就好了,可题意偏偏有,怎么办呢?

我的方法是用两棵树,一棵称为奇树,一棵为偶树

举个栗子来说明:

我需要将2结点加上一个val,我们发现,2这个结点在第2层,“”第2层“中的2是偶数,那么就在偶树上进行操作

将偶树中,2到11加上一个val

同样,如果这个结点在奇数层(比如14,在第3层),就在奇树上进行操作

然后,查询的时候

如果now在偶数层,ans=初始值+偶树上now的值-奇树上now的值

如果now在奇数层,ans=初始值+奇树上now的值-偶树上now的值

具体为什么这样子可以成功,相信大家都能够看出来吧。我希望花力气再去说明了


上面方法所需要的  记录每个原树结点对应现树的哪个结点,现树每个结点的子树最远到哪个结点,现树每个结点在哪一层

这些方法,都可以用dfs搜索来实现,注意标记染色即可,具体怎么写 我就不提了


该方法的时间复杂度是:O(n*logn),n为原树结点的个数


代码如下:

//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi acos(-1.0)
#define eps 1e-6
#define MOD 1000000007
#define MAXN 200100
#define N
#define M
struct Segment_Tree
{
    int left,right;
    ll sum,lazy;
}eventree[4*MAXN],oddtree[4*MAXN];
void plant_tree(int id,int l,int r,Segment_Tree *tree){
    tree[id].left=l,tree[id].right=r;
    tree[id].lazy=0;
    if(l==r){
        tree[id].sum=0;
        return;
    }
    int mid=(l+r)>>1;
    plant_tree(2*id,l,mid,tree);
    plant_tree(2*id+1,mid+1,r,tree);
    tree[id].sum=tree[2*id].sum+tree[2*id+1].sum;
}
void push_down(int id,Segment_Tree *tree){//lazy operation;
    if(!tree[id].lazy) return;
    tree[id].sum+=(tree[id].right-tree[id].left+1)*tree[id].lazy;
    if(tree[id].left==tree[id].right){
        tree[id].lazy=0;
        return;
    }
    tree[2*id].lazy+=tree[id].lazy;
    tree[2*id+1].lazy+=tree[id].lazy;
    tree[id].lazy=0;
}
void update_leaf(int id,int l,int r,ll val,Segment_Tree *tree){
    if(tree[id].left==l && tree[id].right==r){
        tree[id].lazy+=val;
        push_down(id,tree);
        return;
    }
    push_down(id,tree);
    int mid=(tree[id].left+tree[id].right)>>1;
    if(r<=mid) update_leaf(2*id,l,r,val,tree);
    else if(mid<l) update_leaf(2*id+1,l,r,val,tree);
    else{
        update_leaf(2*id,l,mid,val,tree);
        update_leaf(2*id+1,mid+1,r,val,tree);
    }
    tree[id].sum=tree[2*id].sum+tree[2*id+1].sum;
}
ll query(int id,int l,int r,Segment_Tree *tree){
    if(tree[id].left==l && tree[id].right==r){
        push_down(id,tree);
        return tree[id].sum;
    }
    push_down(id,tree);
    int mid=(tree[id].left+tree[id].right)>>1;
    if(r<=mid) return query(2*id,l,r,tree);
    else if(mid<l) return query(2*id+1,l,r,tree);
    else return query(2*id,l,mid,tree)+query(2*id+1,mid+1,r,tree);
}
int n,m,a[MAXN];
struct Edge{//It's a tree by description
    int from,to,next;
}edge[2*MAXN];
int head[MAXN],w,u,v;
void add_edge(int from,int to){
    w+=1;
    edge[w].from=from;
    edge[w].to=to;
    edge[w].next=head[from];
    head[from]=w;
}
bool vis[MAXN];
int num_node,nodemap[MAXN],nodeupto[MAXN],nodefloor[MAXN];
void dfs(int now,int floor){
    vis[now]=true;
    nodemap[now]=++num_node;
    int i,to;
    for(i=head[now];i!=-1;i=edge[i].next){
        to=edge[i].to;
        if(vis[to]) continue;
        dfs(to,floor+1);
    }
    nodefloor[nodemap[now]]=floor;
    nodeupto[nodemap[now]]=num_node;
}
int main()
{
    cin>>n>>m;
    repin(i,1,n){
        scanf("%d",a+i);
    }
    w=0;
    repin(i,1,n){
        head[i]=-1;
        vis[i]=false;
    }
    repin(i,1,n-1){
        scanf("%d %d",&u,&v);
        add_edge(u,v);
        add_edge(v,u);
    }
    num_node=0;
    dfs(1,1);
    plant_tree(1,1,n,eventree),plant_tree(1,1,n,oddtree);
    repin(ii,1,m){
        int type,x,val,from,to,now;
        ll ans;
        scanf("%d",&type);
        switch(type){
            case 1:{
                scanf("%d %d",&x,&val);
                from=nodemap[x];
                to=nodeupto[from];
                if(nodefloor[from]%2==0) update_leaf(1,from,to,val,eventree);
                else update_leaf(1,from,to,val,oddtree);
                break;
            }
            case 2:{
                scanf("%d",&x);
                now=nodemap[x];
                ans=a[x];
                if(nodefloor[now]%2==0) ans+=query(1,now,now,eventree)-query(1,now,now,oddtree);
                else ans+=query(1,now,now,oddtree)-query(1,now,now,eventree);
                printf("%lld\n",ans);
            }
        }
    }
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值