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<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
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<n;i++)//输入
    {
        int u,v;
        scanf("%d%d",&u,&v);
        Add_Edge(u,v);
        Add_Edge(v,u);
    }
    T[1].f=0;//默认1为根节点
    T[1].depth=1;
    dfs1(1,1);//第一遍dfs
    cnt=0;//标号置为0
    dfs2(1,1);//第二遍dfs
    for (int i=1;i<=n;i++)//输入点权
        scanf("%lld",&W[i]);
    for (int i=1;i<=n;i++)
        Update(1,n,1,dfn[i],W[i]);//将点权按照新编号放入线段树
    int qus;
    scanf("%d",&qus);
    while (qus--)//处理操作
    {
        char str[10];
        scanf("%s",str);
        if (str[0]=='Q')
        {
            int u,v;
            scanf("%d%d",&u,&v);
            if (str[1]=='M')
                printf("%lld\n",LCA_max(u,v));//查询最大值
            else
                printf("%lld\n",LCA_sum(u,v));//查询和
        }
        else
        {
            int pos,key;
            scanf("%d%d",&pos,&key);
            pos=dfn[pos];
            Update(1,n,1,pos,key);//单点修改
        }
    }
    return 0;
}
void Add_Edge(int u,int v)//在图中加一条边
{
    cnt++;
    E[cnt].nex=Head[u];
    Head[u]=cnt;
    E[cnt].u=u;
    E[cnt].v=v;
    return;
}
void dfs1(int u,int father)//第一遍dfs
{
    T[u].sz=1;//初始化基本信息
    T[u].hson=0;
    for (int i=Head[u];i!=-1;i=E[i].nex)
    {
        int v=E[i].v;
        if (v==father)
            continue;
        T[v].f=u;//将子节点信息更新
        T[v].depth=T[u].depth+1;
        dfs1(v,u);
        T[u].sz=T[u].sz+T[v].sz;
        if ((T[u].hson==0)||(T[T[u].hson].sz<T[v].sz))//求出重儿子
            T[u].hson=v;
    }
    return;
}
void dfs2(int u,int Top)//第二遍dfs,Top是当前传下来的能到达的重链顶端,当u==Top时,为轻链
{
    cnt++;
    dfn[u]=cnt;//分配新编号
    T[u].top=Top;//分配Top
    if (T[u].hson==0)//没有重儿子,说明到了叶子节点
        return;
    dfs2(T[u].hson,Top);//让重儿子继承重链
    for (int i=Head[u];i!=-1;i=E[i].nex)
    {
        int v=E[i].v;
        if ((v==T[u].f)||(v==T[u].hson))
            continue;
        dfs2(v,v);//其他儿子开轻链
    }
    return;
}
ll LCA_max(int u,int v)//求解u到v路径上的最大点权
{
    ll mx=-inf;
    while (T[u].top!=T[v].top)
    {
        if (T[T[u].top].depth<T[T[v].top].depth)//保证每一次u的深度尽量大
            swap(u,v);
        mx=max(mx,Query_max(1,n,1,dfn[T[u].top],dfn[u]));//让深度大的向上跳,注意这里要转成线段树中的编号
        u=T[T[u].top].f;
    }
    if (dfn[v]<dfn[u])//保证u的深度更小
        swap(u,v);
    mx=max(mx,Query_max(1,n,1,dfn[u],dfn[v]));//查询链
    return mx;
}
ll LCA_sum(int u,int v)//求解u到v上的点权之和,与上面基本一样
{
    ll sum=0;
    while (T[u].top!=T[v].top)
    {
        if (T[T[u].top].depth<T[T[v].top].depth)
            swap(u,v);
        sum=sum+Query_sum(1,n,1,dfn[T[u].top],dfn[u]);
        u=T[T[u].top].f;
    }
    if (dfn[v]<dfn[u])
        swap(u,v);
    sum=sum+Query_sum(1,n,1,dfn[u],dfn[v]);
    return sum;
}
void Update(int l,int r,int now,int pos,int key)//线段树单点修改
{
    if (l==r)
    {
        Seg[now].mx=Seg[now].sum=key;
        return;
    }
    int mid=(l+r)/2;
    if (pos<=mid)
        Update(l,mid,now*2,pos,key);
    if (pos>=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));
}
Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树)的更多相关文章
- HDU5266 LCA 树链剖分LCA 线段树
		HDU5266 LCA Description 给一棵 n 个点的树,Q 个询问 [L,R] : 求点 L , 点 L+1 , 点 L+2 -- 点 R 的 LCA. Input 多组数据. The ... 
- bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)
		1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 10677 Solved: 4313[Submit ... 
- bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)
		3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1272 Solved: 451[Submit][Status ... 
- bzoj 2243 [SDOI2011]染色(树链剖分,线段树)
		2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4637 Solved: 1726[Submit][Status ... 
- HDU 4366 Successor(树链剖分+zkw线段树+扫描线)
		[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=4366 [题目大意] 有一个公司,每个员工都有一个上司,所有的人呈树状关系,现在给出每个人的忠诚值和 ... 
- 【洛谷5439】【XR-2】永恒(树链剖分,线段树)
		[洛谷5439][XR-2]永恒(树链剖分,线段树) 题面 洛谷 题解 首先两个点的\(LCP\)就是\(Trie\)树上的\(LCA\)的深度. 考虑一对点的贡献,如果这两个点不具有祖先关系,那么这 ... 
- bzoj 4034 [HAOI2015] T2(树链剖分,线段树)
		4034: [HAOI2015]T2 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1536 Solved: 508[Submit][Status] ... 
- poj 3237 Tree(树链剖分,线段树)
		Tree Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 7268 Accepted: 1969 Description ... 
- 【BZOJ3531】旅行(树链剖分,线段树)
		[BZOJ3531]旅行(树链剖分,线段树) 题面 Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教 ... 
随机推荐
- 如何设置本机电脑的固定IP地址?
			最近使用Loadrunner需要用到IP欺骗,但是我打开设置IP欺骗却提示我:IP向导不支持启用DHCP的网卡.您的卡启用了DHCP或者配置了无效设置.请与系统管理员联系. 我就方了,于是百度了一下, ... 
- mysql操作命令梳理(2)-alter(update、insert)
			在mysql运维操作中会经常使用到alter这个修改表的命令,alter tables允许修改一个现有表的结构,比如增加或删除列.创造或消去索引.改变现有列的类型.或重新命名列或表本身,也能改变表的注 ... 
- Websocket(一)——原理及基本属性和方法
			初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处? 答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起. 举例 ... 
- 12.13 Daily Scrum
			现在已经可以实现在应用中直接通过WebView浏览餐厅的网页,而不用再调用手机的浏览器. 收藏夹的功能也基本实现,接下来的目标时将收藏夹与每一个用户关联. Today's Task Tomorro ... 
- 个人项目Week1
			一.项目时间规划与实际用时 PSP2.1 Personal Software Process Stages 预计时间/h 实际时间/h Planning 计划 · Estimate · 估计这 ... 
- jdk自带的jvisualvm-监控远程linux
			简介 jdk有好多自带的工具比如jconsole.jvisualvm.jstatd等 Windows的路径:%JAVA_HOME/bin/目录下,配置好环境变量直接用cmd执行jvisualvm命令即 ... 
- 在Java中执行Tomcat中startup.bat
			问题:更改数据库时,需要重启Tomcat服务器,才能把更改后的数据加载到项目中.于是想每次更改数据库时,都调用Java方法,重启Tomcat 代码: Process process = Runtime ... 
- FMDB数据库升级
			FMDBMigrationManager 是与FMDB结合使用的一个第三方,可以记录数据库版本号并对数据库进行数据库升级等操作.首先要集成FMDB和FMDBMigrationManager,建议使用c ... 
- Maven的课堂笔记3
			8 仓库管理 仓库可以分为三种:1.本地仓库(本机).2.私服(公司局域网内的maven服务器).3.中央仓库(互联上,例如 struts2官网,或者hibernate官网) 可以根据maven坐标定 ... 
- HTTP Client使用总结
			1.如果服务器使用HTTPS协议,使用HTTPClient的话,需要配以EasySSLProtocolSocketFactory 2.Tomcat对HTTP的Post和Get请求处理是不一样的.Spr ... 
