题目链接:https://loj.ac/problem/2195
解题思路
树链剖分+动态开点线段树。可以通过树链剖分将问题转化成询问序列上与起点相同宗教的权值总和及最大值,并且支持单点更新。这个问题可以用线段树解决,但一般线段树要开四倍空间,而且要维护不同宗教,所以空间肯定不够。但是我们可以动态开点,树上每个点最多对应logn个点,所以动态开点最多也就nlogn个点,空间就够了,就是牺牲了常数的时间去开点,在这是可以接受的。
AC代码

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e5+5;
const int maxm=2e7+5;
const int inf=0x3f3f3f3f;
struct node
{int to,next;
}edge[maxn<<1];//链式前向星
int head[maxn],num[maxn],root[maxn];
struct Node
{int l,r,Max,Sum;//l是左儿子编号,r是右儿子的编号
}tree[maxm];
int cnt,n,q,len;
int get()//快读
{char c;int sign=1;while((c=getchar())<'0'||c>'9')if(c=='-')sign=-1;int res=c-'0';while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';return res*sign;
}
void add(int x,int y)
{edge[++cnt].to=y;edge[cnt].next=head[x];head[x]=cnt;
}
int fa[maxn];//x在树中的父亲
int dep[maxn];//x在树中的深度
int size[maxn];//x的子树结点数(子树大小)
int son[maxn];//x的重儿子,即u->son[u]是重边
int top[maxn];//x所在重路径的顶部顶点(深度最小)
int seg[maxn];//x在线段树中的位置(下标)
int rev[maxn];//线段树中第x位置对应的树中结点编号,rev[seg[x]]=x
int w[maxn];
int zj[maxn];
int Ssum,Mmax;
void update(int &rt,int L,int R,int val,int pos)//val是评级,os你要添加的点的fs序
{if(!rt)rt=++len;tree[rt].Max=max(tree[rt].Max,val);tree[rt].Sum+=val;if(L==R)return ;int mid=(L+R)>>1;if(mid>=pos)update(tree[rt].l,L,mid,val,pos);elseupdate(tree[rt].r,mid+1,R,val,pos);
}
void remove(int &rt,int L,int R,int pos)
{if(L==R){tree[rt].Sum=tree[rt].Max=0;return ;}int mid=(L+R)>>1;if(mid>=pos)remove(tree[rt].l,L,mid,pos);elseremove(tree[rt].r,mid+1,R,pos);tree[rt].Sum=tree[tree[rt].l].Sum+tree[tree[rt].r].Sum;tree[rt].Max=max(tree[tree[rt].l].Max,tree[tree[rt].r].Max);
}
int querySum(int rt,int lb,int rb,int L,int R)
{if(R<lb||L>rb)return 0;if(L<=lb&&R>=rb)return tree[rt].Sum;int mid=(lb+rb)>>1;return querySum(tree[rt].l,lb,mid,L,R)+querySum(tree[rt].r,mid+1,rb,L,R);
}
int queryMax(int rt,int lb,int rb,int L,int R)
{if(R<lb||L>rb)return 0;if(L<=lb&&R>=rb)return tree[rt].Max;int mid=(lb+rb)>>1;return max(queryMax(tree[rt].l,lb,mid,L,R),queryMax(tree[rt].r,mid+1,rb,L,R));
}
void dfs1(int u,int f)//第一遍dfs,算出fa[],dep[],size[],son[]
{size[u]=1;fa[u]=f;dep[u]=dep[f]+1;for(int i=head[u];i;i=edge[i].next){int v=edge[i].to;if(v==f)continue;dfs1(v,u);size[u]+=size[v];//计算sizeif(size[v]>size[son[u]])//求重儿子son[u]=v;}
}
void dfs2(int u,int f)//第二遍dfs,算出top[],seg[],rev[]
{if(son[u])//先走重儿子,使重路径在线段树中的位置连续{seg[son[u]]=++seg[0];//根无法在这里赋值,所以根要在主程序赋值top[son[u]]=top[u];//如果(u,v)为重边,那么u和v在同一条重路径上rev[seg[0]]=son[u];dfs2(son[u],u);}for(int i=head[u];i;i=edge[i].next){int v=edge[i].to;if(top[v])//top[v]有值即是被遍历过continue;seg[v]=++seg[0];rev[seg[0]]=v;top[v]=v;//如果(u,v)是轻边,则v就是其所在重路径的顶部结点dfs2(v,u);}
}
void askSum(int x,int y,int zj)//路径询问
{int fx=top[x],fy=top[y];while(fx!=fy){if(dep[fx]<dep[fy])//选择深度较大的往上跳{swap(x,y);swap(fx,fy);}Ssum+=querySum(root[zj],1,n,seg[fx],seg[x]);//重路径对应区间[seg[fx],seg[x]]x=fa[fx];fx=top[x];}if(dep[x]>dep[y])swap(x,y);Ssum+=querySum(root[zj],1,n,seg[x],seg[y]);
}
void askMax(int x,int y,int zj)
{int fx=top[x],fy=top[y];while(fx!=fy){if(dep[fx]<dep[fy])//选择深度较大的往上跳{swap(x,y);swap(fx,fy);}Mmax=max(Mmax,queryMax(root[zj],1,n,seg[fx],seg[x]));//重路径对应区间[seg[fx],seg[x]]x=fa[fx];fx=top[x];}if(dep[x]>dep[y])swap(x,y);Mmax=max(Mmax,queryMax(root[zj],1,n,seg[x],seg[y]));
}
int main()
{n=get();q=get();for(int i=1;i<=n;++i){w[i]=get();zj[i]=get();}for(int i=1;i<n;++i){int x,y;x=get();y=get();add(x,y);add(y,x);}dfs1(1,0);seg[0]=seg[1]=top[1]=rev[1]=1;//根结点所在重路径的顶部结点一定还是根结点dfs2(1,0);for(int i=1;i<=n;++i)update(root[zj[i]],1,n,w[i],seg[i]);char str[20];while(q--){int x,y;scanf("%s",str);x=get();y=get();if(str[0]=='C'){if(str[1]=='C'){remove(root[zj[x]],1,n,seg[x]);update(root[y],1,n,w[x],seg[x]);zj[x]=y;}else{remove(root[zj[x]],1,n,seg[x]);update(root[zj[x]],1,n,y,seg[x]);w[x]=y;}}else{if(str[1]=='S'){Ssum=0;askSum(x,y,zj[x]);printf("%d\n",Ssum);}else{Mmax=0;askMax(x,y,zj[x]);printf("%d\n",Mmax);}}}return 0;
}

树链剖分(四)——旅行相关推荐

  1. 【bzoj 3531】 [Sdoi2014]旅行(树链剖分+树套树)

    3531: [Sdoi2014]旅行 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 1197  Solved: 576 [Submit][Statu ...

  2. P3313-[SDOI2014]旅行【树链剖分,线段树】

    正题 题目链接:https://www.luogu.com.cn/problem/P3313 题目大意 nnn个点的一棵树,每个点有一个颜色和权值,有操作 修改一个点的权值 修改一个点的颜色 询问一条 ...

  3. 归纳(四):树链剖分

    剖分的意义 能用线段树搞想要的信息. (其实可以只用来求LCA) 需要的东西 七个数组,在两次dfs中处理出来. dfs1: dep[]:深度 fa[]:父亲 son[]:重儿子 siz[]:子树大小 ...

  4. 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree...

    原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...

  5. 模板 - 图论 - 树链剖分

    今天来啃一下这个树剖吧. 模板题是要求这四个问题: 将树从x到y结点最短路径上所有节点的值都加上z 求树从x到y结点最短路径上所有节点的值之和 将以x为根节点的子树内所有节点值都加上z 求以x为根节点 ...

  6. 软件包管理器(树链剖分)

    Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个软件包的安 ...

  7. 简单dfs序 + 树链剖分

    树链剖分 DFS序 先来讲一讲DFS序是什么东西,直接上图,方便理解. 估计巨巨们应该知道了DFS序的两个重要的东西,in,outin,outin,out数组. ininin数组就是这个点进入DFS的 ...

  8. 树链剖分概念及模板 + 例题 [POJ3237 tree + 软件包管理器]

    文章目录 概念 模板 例题1:软件包管理器 题目 题解 代码实现 例题2:POJ3237 tree 题目 题解 代码实现 概念 树链剖分主要是用于解决以下这两个问题. 1.更改树上点x到点y的最短路径 ...

  9. bzoj 4196 树链剖分 模板

    [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 2135  Solved: 1232 [Submit][Status] ...

最新文章

  1. Linux软raid创建和维护
  2. mysql 切换数据库方案
  3. OpenStack的部署T版(五)——Nova组件
  4. java控制图片移动_多线程控制图片移动
  5. Java案例:集合的Stream方法
  6. html下拉框传递参数,HTML通过事件传递参数到js详解及实例
  7. python库skimage 将针对灰度图像的滤波器用于RGB图像
  8. [JNI] 开发基础(5)内存分配
  9. Ubuntu 18.04联网问题(已解决)
  10. 正则入门 掌握字符组和量词
  11. .NET报表设计器ActiveReports入门:操作界面详解
  12. 【verbs】ibv_modify_qp()|RDMA
  13. 智和网管平台SugarNMS政府部门综合网管
  14. codeforces 884F 费用流,图解很清晰
  15. 创建一个最简单的win32应用程序
  16. 插件小王子的插件源码汇总
  17. Node.js的线程和进程*2014年4月的文章
  18. 购物清单(Python实现)
  19. Easypack之Alpine容器系列:Sonarqube
  20. 多个资本强力支持,高仙机器人完成12亿元C轮融资

热门文章

  1. 基于51单片机 超声波测距 倒车雷达
  2. 洛谷 P1337 [JSOI2004]平衡点 / 吊打XXX
  3. 这些愚蠢的问题你犯过吗?
  4. Svn版本控制工具的作用和应用
  5. 用渣渣示波器窥探MIPI信号的大概样子
  6. 候选码的求解基本方法集合
  7. STM32 HAL库 实现基于SPI模式的SD卡、TF卡FATS文件系统+模拟U盘的应用笔记
  8. Atomikos的使用过程中要注意的事
  9. 【Unity 03】高通 AR SDK基本使用以及项目发布
  10. spring的依赖注入方式