Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树)...

Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树)

Description

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作:
I. CHANGE u t : 把结点u的权值改为t

II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值

III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

Input

输入文件的第一行为一个整数n,表示节点的个数。
接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。
接下来一行n个整数,第i个整数wi表示节点i的权值。
接下来1行,为一个整数q,表示操作的总数。
接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。

Output

对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

Sample Input

4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

Sample Output

4
1
2
2
10
6
5
6
5
16

Http

Luogu:https://www.luogu.org/problem/show?pid=2590
HYSBZ:http://www.lydsy.com/JudgeOnline/problem.php?id=1036

Source

树链剖分,最近公共祖先LCA,线段树

解决思路

这是一道树链剖分入门题。
考虑将树按照子树大小剖分成轻链和重链,然后用线段树来维护区间和和区间最大值。
需要注意的是,在树上进行链的跳转时,使用原编号,而若要用线段树查询或修改线段树时,要使用线段树中的编号(即第2次dfs中给点新分配的编号)

代码

#include
#include
#include
#include
#include
using namespace std;

#define ll long long

const int maxN=350000;
const int inf=2147483647;

class Edge//边
{
public:
    int u,v,nex;
};

class SegmentTree//线段树
{
public:
    ll mx,sum;//维护最大值和区间和
    SegmentTree()
        {
            mx=0;
            sum=0;
        }
};

class Tree_Chain//树链剖分中需要维护的一些值
{
public:
    int f,sz,top,hson,depth;//父节点,大小,向上跳的最高位置,重儿子,深度
};

int n;
//Tree 原树的一些数据
int cnt;
int Head[maxN];
Edge E[maxN*2];
ll W[maxN];//点权
Tree_Chain T[maxN];//树链剖分维护的值
int dfn[maxN];//记录树链剖分后的点的新编号(按照先重链再轻链)
SegmentTree Seg[maxN*4];//线段树

void Add_Edge(int u,int v);
//Tree_Chain
void dfs1(int u,int father);//第一遍dfs求出T中的一些数据
void dfs2(int u,int Top);//第二遍dfs构造Top和分配新编号
ll LCA_sum(int u,int v);//求解区间和
ll LCA_max(int u,int v);//求解区间最大值
//SegmentTree
void Update(int l,int r,int now,int pos,int key);//线段树单点更新
ll Query_sum(int l,int r,int now,int ql,int qr);//查询区间和
ll Query_max(int l,int r,int now,int ql,int qr);//查询区间最大值

int main()
{
    cnt=0;
    memset(Head,-1,sizeof(Head));
    scanf("%d",&n);
    for (int i=1;i=mid+1)
        Update(mid+1,r,now*2+1,pos,key);
    Seg[now].sum=Seg[now*2].sum+Seg[now*2+1].sum;
    Seg[now].mx=max(Seg[now*2].mx,Seg[now*2+1].mx);
    return;
}

ll Query_sum(int l,int r,int now,int ql,int qr)//线段树查询区间和
{
    if ((l==ql)&&(r==qr))
        return Seg[now].sum;
    int mid=(l+r)/2;
    if (qr<=mid)
        return Query_sum(l,mid,now*2,ql,qr);
    else if (ql>=mid+1)
        return Query_sum(mid+1,r,now*2+1,ql,qr);
    else
        return Query_sum(l,mid,now*2,ql,mid)+Query_sum(mid+1,r,now*2+1,mid+1,qr);
}

ll Query_max(int l,int r,int now,int ql,int qr)//线段树查询区间最大值
{
    if ((l==ql)&&(r==qr))
        return Seg[now].mx;
    int mid=(l+r)/2;
    if (qr<=mid)
        return Query_max(l,mid,now*2,ql,qr);
    if (ql>=mid+1)
        return Query_max(mid+1,r,now*2+1,ql,qr);
    return max(Query_max(l,mid,now*2,ql,mid),Query_max(mid+1,r,now*2+1,mid+1,qr));
}

转载于:https://www.cnblogs.com/SYCstudio/p/7629164.html

你可能感兴趣的:(php)