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;
}

时间: 2024-10-02 06:07:17

light oj 1348 树链剖分(单点更新区间求值)的相关文章

SPOJ - QTREE(树链剖分+单点更新+区间最大值查询)

题意:给出n个点n-1条边的树,有两个操作,一个是查询节点l到r的边的最大值,然后指定边的更改权值. 题解:差不多是树链剖分的模版题,注意每个点表示的边是连向其父亲节点的边. #include <iostream> #include <cstring> #include <cstdio> using namespace std; const int M = 1e4 + 10; struct Edge { int v , next; }edge[M << 1]

FZU2082 树链剖分(单点更新区间求值)

http://acm.fzu.edu.cn/problem.php?pid=2082  Problem Description 有n座城市,由n-1条路相连通,使得任意两座城市之间可达.每条路有过路费,要交过路费才能通过.每条路的过路费经常会更新,现问你,当前情况下,从城市a到城市b最少要花多少过路费.  Input 有多组样例,每组样例第一行输入两个正整数n,m(2 <= n<=50000,1<=m <= 50000),接下来n-1行,每行3个正整数a b c,(1 <=

HDU 5274 Dylans loves tree(LCA+dfs时间戳+成段更新 OR 树链剖分+单点更新)

Problem Description Dylans is given a tree with N nodes. All nodes have a value A[i].Nodes on tree is numbered by 1∼N. Then he is given Q questions like that: ①0 x y:change node x′s value to y ②1 x y:For all the value in the path from x to y,do they

poj 2763 Housewife Wind(树链剖分+单点查询+区间修改)

题目链接:http://poj.org/problem?id=2763 题意:给一个数,边之间有权值,然后两种操作,第一种:求任意两点的权值和,第二,修改树上两点的权值. 题解:简单的树链剖分. #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int M = 1e5 + 10; struct Edge { int v , next; }edge[M &

【HDU】1754 I hate it ——线段树 单点更新 区间最值

I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 37448    Accepted Submission(s): 14816 Problem Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少.这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要

一维 + 二维树状数组 + 单点更新 + 区间更新 详解

树状数组详解: 假设一维数组为A[i](i=1,2,...n),则与它对应的树状数组C[i](i=1,2,...n)是这样定义的: C1 = A1 C2 = A1 + A2 C3 = A3 C4 = A1 + A2 + A3 + A4 C5 = A5 C6 = A5 + A6 ................. C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 ................ 如图可知: 为奇数的时候他是代表他本身,而为偶数的时候则是代表着自

hdu2795(线段树单点更新&amp;区间最值)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2795 题意:有一个 h * w 的板子,要在上面贴 n 条 1 * x 的广告,在贴第 i 条广告时要尽量将其靠上贴,并输出其最上能贴在哪个位置: 思路:可以将每行剩余空间大小存储到一个数组中,那么对于当前 1 * x 的广告,只需找到所有剩余空间大于的 x 的行中位置最小的即可: 不过本题数据量为 2e5,直接暴力因该会 tle.可以用个线段树维护一下区间最大值,然后查询时对线段树二分即可: 代码

LIGHTOJ 1348 树链剖分

点击打开链接 题意:给一个树和树上点的权值,两个操作,一个是将点的权值修改,一个是询问u到v的点权和 思路:模版题,kuangbin开的这套题为什么这么模版的写的人这么少,可能大神们不屑于写这种题吧(弱哭) #pragma comment(linker, "/STACK:1024000000,1024000000") #include <vector> #include <stdio.h> #include <string.h> #include &

hdu 1754 I Hate It(线段树之 单点更新+区间最值)

I Hate It                                                                             Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Description 非常多学校流行一种比較的习惯.老师们非常喜欢询问.从某某到某某其中,分数最高的是多少. 这让非常多学生非常反感. 无论你