light oj 1348 树链剖分(单点更新区间求值)

http://lightoj.com/volume_showproblem.php?problem=1348

Finally the Great Magical Lamp was in Aladdin's hand. Now he wanted to return home. But he didn't want to take any help from the Genie because he thought that it might be another adventure for him. All he remembered was the paths he had taken to reach there. But since he took the lamp, all the genies in the cave became angry and they were planning to attack. As Aladdin was not afraid, he wondered how many genies were there. He summoned the Genie from the lamp and asked this.

Now you are given a similar problem. For simplicity assume that, you are given a tree (a connected graph with no cycles) with n nodes, nodes represent places, edges represent roads. In each node, initially there are an arbitrary number of genies. But the numbers of genies change in time. So, you are given a tree, the number of genies in each node and several queries of two types. They are:

1)      0 i j, it means that you have to find the total number of genies in the nodes that occur in path from node i to j (0 ≤ i, j < n).

2)      1 i v, it means that number of genies in node i is changed to v (0 ≤ i < n, 0 ≤ v ≤ 1000).

Input

Input starts with an integer T (≤ 10), denoting the number of test cases.

Each case starts with a blank line. Next line contains an integer n (2 ≤ n ≤ 30000). The next line contains n space separated integers between 0 and 1000, denoting the number of genies in the nodes respectively. Then there are n-1 lines each containing two integers: u v (0 ≤ u, v < n, u ≠ v) meaning that there is an edge from node u and v. Assume that the edges form a valid tree. Next line contains an integer q (1 ≤ q ≤ 105) followed by q lines each containing a query as described above.

Output

For each case, print the case number in a single line. Then for each query 0 i j, print the total number of genies in the nodes that occur in path i to j.

Sample Input

Output for Sample Input

1

 

4

10 20 30 40

0 1

1 2

1 3

3

0 2 3

1 1 100

0 2 3

Case 1:

90

170

Note

Dataset is huge, use faster I/O methods.

/**
light oj 1348  树链剖分(单点更新区间求值)
题目大意:给定一个树,对树上的指定单点动态更新,对指定两点之间的和动态查询
解题思路:树链剖分,注意节点从0开始
*/
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn=30005;
int fa[maxn],dep[maxn],top[maxn],son[maxn],num[maxn],siz[maxn];
int a[maxn],tree[maxn*4],Hash[maxn];
int n,m,z;
int head[maxn],ip;

void init()
{
    memset(tree,0,sizeof(tree));
    memset(head,-1,sizeof(head));
    ip=0;
}

struct note
{
    int v,next;
}edge[maxn*2];

void addedge(int u,int v)
{
    edge[ip].v=v,edge[ip].next=head[u],head[u]=ip++;
}

void dfs(int u,int pre)
{
    son[u]=0,siz[u]=1,dep[u]=dep[pre]+1,fa[u]=pre;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==pre)continue;
        dfs(v,u);
        siz[u]+=siz[v];
        if(siz[son[u]]<siz[v])
            son[u]=v;
    }
   /// printf("%d fa son siz dep %d %d %d %d\n",u,fa[u],son[u],siz[u],dep[u]);
}

void init_que(int u,int tp)
{
    num[u]=++z,top[u]=tp,Hash[z]=u;
    if(son[u])
    {
        init_que(son[u],tp);
    }
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==son[u]||v==fa[u])continue;
        init_que(v,v);
    }
    ///printf("%d num top %d %d\n",u,num[u],top[u]);
}

void push_up(int root)
{
    tree[root]=tree[root<<1]+tree[root<<1|1];
}
void build(int root,int l,int r)
{
    if(l==r)
    {
        tree[root]=a[Hash[l]];
        return;
    }
    int mid=(l+r)>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);
    push_up(root);
}

void update(int root,int l,int r,int loc,int z)
{
    if(l>loc||r<loc)return;
    if(l==r)
    {
        tree[root]=z;
        return;
    }
    int mid=(l+r)>>1;
    update(root<<1,l,mid,loc,z);
    update(root<<1|1,mid+1,r,loc,z);
    push_up(root);
}
int query(int root,int l,int r,int x,int y)
{
    if(x>r||y<l)return 0;
    if(x<=l&&r<=y)
    {
        return tree[root];
    }
    int mid=(l+r)>>1;
    return query(root<<1,l,mid,x,y)+query(root<<1|1,mid+1,r,x,y);
}
int main()
{
    ///freopen("data.txt","r",stdin);
    int T,tt=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        init();
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            x++,y++;
            addedge(x,y);
            addedge(y,x);
        }
        int root=(n+1)>>1;
        z=0,siz[0]=0,dep[0]=0;
        dfs(root,0);
        init_que(root,root);
        build(1,1,z);
        scanf("%d",&m);
        printf("Case %d:\n",++tt);
        while(m--)
        {
            int t,x,y;
            scanf("%d%d%d",&t,&x,&y);
            if(t==0)
            {
                x++,y++;
                int f1=top[x],f2=top[y],sum=0;
                while(f1!=f2)
                {
                    if(dep[f1]<dep[f2])
                    {
                        swap(f1,f2);
                        swap(x,y);
                    }
                    sum+=query(1,1,z,num[f1],num[x]);
                    x=fa[f1],f1=top[x];
                }
                if(dep[x]>dep[y])
                    swap(x,y);
                sum+=query(1,1,z,num[x],num[y]);
                printf("%d\n",sum);
            }
            else
            {
                x++;
                update(1,1,z,num[x],y);
            }
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值