树链剖分

(树链剖分+线段树)POJ - 3237 Tree

≡放荡痞女 提交于 2020-03-28 14:25:18
前言: 一直听说树链剖分-树链剖分,现在见识一下,,,感觉不是很难0.0 看了一下kuangbin模板基本秒懂 对于点,按重边优先给予每个点一个编号,对于一条重链上的点,编号则是连续的,将所有编号映射到线段树上,即可进行一切区间操作。 对于边的处理,我们将所有边对应到这条边节点更深的那个点上即可。 如果需要的操作只有求和,和单点更新/区间更新,直接用树状数组也是可以的,可能常数大那么一点点。 如果还需要更加强大的操作,显然用splay树维护也是可以的。。 比如树链上所有权值翻转等等。。不过,,这样差不多应该就快到LCT了。。虽然自己不会。。 不过过几天就该看了。 题意: 一棵树,每条边有一个权值,三种操作: 1、改变第i条边权值为v 2、将节点a到节点b之间的边权值取反 3、查询a到b之间最大的边权值 分析: 显然就是普通的树链剖分和线段树 但是要记得pushdown, 而对于lazy标记rev,当传下去的时候,不应该直接赋值为1或者0,而应该直接取反,用异或即可。 代码: 1 #include <math.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <time.h> 6 #include <algorithm> 7 #include <iostream> 8

树链剖分【洛谷P4114】 Qtree1

…衆ロ難τιáo~ 提交于 2020-03-15 11:30:50
P4114 Qtree1 题目描述 给定一棵n个节点的树,有两个操作: CHANGE i ti 把第i条边的边权变成ti QUERY a b 输出从a到b的路径中最大的边权,当a=b的时候,输出0 码农题。 话说我码得变快了啊,虽然跟顾z吹45分钟码完Qtree没有完成,不过总共用了55分钟还是不长的嘿嘿。 code: #include <iostream> #include <cstdio> #define ls(o) o<<1 #define rs(o) o<<1|1 #define int long long using namespace std; const int wx=500017; int head[wx],dfn[wx],size[wx],f[wx][23],dis[wx][23],dep[wx]; int son[wx],a[wx],top[wx],tid[wx]; int n,num,tot; char opt[17]; inline int read(){ int sum=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar(

DFS spanning tree【贪心+树链剖分】

旧时模样 提交于 2020-03-08 03:01:13
题目链接 HDU - 4582 在开始解决这个问题之前,我们来解决如下一个问题: 有一段总长度为N的连续点集,现在给出M个区间段[Li, Ri]使得每个区间段中至少有一个点被染色,问最少需要染色多少个结点?(使用Nlog时间复杂度) 解:这个问题其实是一个贪心的过程,譬如说,我们先对R区间升序排序,然后呢,对于每个Li,我们只要知道它是否是在现在的已有的R区间包含内的,就可知道是否是需要新添加点的了。 回到这道题目上来, 题意 :有N个点构成的树,先给出N-1条树上的边,再给出M-N+1条我们需要查询的边,使得查询边与原树边构成的环上至少有一条边在树上。 那么,这问题岂不是跟之前提出的问题那么相像呢?就是所谓区间应该如何定义? 给出的M-N+1条“查询边”,我们可以定义为原树上的长链,于是乎,就是对这些长链进行之前的染色问题即可了,但是由于是在树上的,这时候应该如何等效过去呢? 首先,升序排序,我对于每条边的头结点进行按照深度的降序排列,保证了贪心的思路跟上是一样的,最深的是一定要取的。 其次,是查询了,怎么知道区间内的点是否被染色了?这里我很简单的使用了树链剖分来解决这个问题,虽然好像直接暴力搜也没有问题(好像,没试过),我用树链剖分来解决查询这个长链是否有边被染色过了,以及给边染色。 #include <iostream> #include <cstdio> #include

树链剖分模板

坚强是说给别人听的谎言 提交于 2020-03-01 17:44:12
树剖例题 然后发现可以替代LCA中查询两点距离,特意来保存下代码模板 我代码中qulen函数 就是查询两点间的距离。 学习博客 #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+10; vector<int>G[N]; int a[N]; int sum[2][4*N]; int n,q; int sz[N],son[N],f[N],d[N],top[N],id[N],cnt; void dfs1(int u,int fat,int dep) { f[u]=fat; d[u]=dep; sz[u]=1; for(int v:G[u]) { if(v==fat) continue; dfs1(v,u,dep+1); sz[u]+=sz[v]; if(sz[v]>sz[son[u]]) son[u]=v; } } void dfs2(int u,int t) { top[u]=t; id[u]=++cnt; if(!son[u]) return ; dfs2(son[u],t); for(int v:G[u]) { if(v==son[u]||v==f[u]) continue; dfs2(v,v); } } void up(int id,int l,int r,int

BZOJ 3531 [Sdoi2014]旅行 树链剖分+动态开点线段树

人盡茶涼 提交于 2020-02-23 11:15:09
题意 S国有N个城市,编号从1到N。城市间用N-1条双向道路连接,满足从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。 为了方便,我们用不同的正整数代表各种宗教, S国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。 在S国的历史上常会发生以下几种事件: “CC x c“:城市x的居民全体改信了c教; “CW x w“:城市x的评级调整为w; “QS x y“:一位旅行者从城市x出发,到城市y,并记下了途中留宿过的城市的评级总和; “QM x y“:一位旅行者从城市x出发,到城市y,并记下了途中留宿过的城市的评级最大值。 由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。 为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。 分析 对每种宗教建一颗动态开点线段树,不动态开点稳Tle+Mle的,然后就树链剖分裸题啊,对于查询操作,查询出发城市的信仰宗教的线段树就行了 这题写完就过编译和样例了

树链剖分详解

给你一囗甜甜゛ 提交于 2020-02-22 06:44:30
  问题是这样的:对于一株树(无向无环连通图),为每个结点分配对应的权重。要求能高效计算任意两个结点之间的路径的各类信息,其中包括路径长度(路径上所有结点的权重加总),路径中最大权重,最小权重等等。到这里一切都还是比较简单的,我们可以利用Tarjan的LCA算法在线性时间复杂度内快速求解。但是如果还要求允许动态修改任意结点的权值,那么问题就不简单了。很容易发现这有些类似于线段树的问题,但是线段树是作用在区间上,而我们的问题是发生在树上的,因此线段树并不适用。而树链剖分则是可以用于求解这类问题的高效算法,其更新权值的时间复杂度为O(log2(n)),而统计路径信息的时间复杂度为O((log2(n))^2)。下面对树链剖分进行讲解。   对于任意一株树,我们记r.size表示以结点r为根的子树中的结点总数,称为r的大小。记r.next表示r的所有子结点中size属性最大的一个结点(如果没有子结点允许留空),称为r的重孩子,而其余子结点称为r的轻孩子,与重孩子相连的边称为重边,而与轻孩子相连的边称为轻边。利用深度优先搜索算法可以在O(n)时间复杂度内计算所有结点的size和next属性。   很容易发现通过将每个结点与其重孩子通过一条特殊的链条连接,那么我们就在图中建立了多条互不相交的链路,每个结点都处于一个链路中(链路可能只有一个结点),这些链路称为重链。每一条重链中所有结点深度不同

树链剖分

蹲街弑〆低调 提交于 2020-02-17 11:30:27
cogs||bzoj1036 树的统计count 题目大意:模板题。 思路:模板题。 #include<iostream> #include<cstdio> #define maxnode 30001 #define mid (l+r)/2 #define inf 2100000000LL using namespace std; struct use{ int fa,top,siz,son,dep,tid; }tree[maxnode]={0}; struct seg{ int maxn,sum; }t[maxnode*4]={0}; int point[maxnode*2]={0},next[maxnode*2]={0},en[maxnode*2],tot=0,tt[maxnode],wi[maxnode]; char ch[10]; bool visit[maxnode]={false}; void add(int st,int enn) { ++tot;next[tot]=point[st];point[st]=tot;en[tot]=enn; ++tot;next[tot]=point[enn];point[enn]=tot;en[tot]=st; } void findc(int u,int fa,int depth) { int i,j,maxsiz=0; visit

两道树链剖分题目

旧巷老猫 提交于 2020-02-07 20:56:13
2020 CCPC-Wannafly Winter Camp Day2 --- F. 采蘑菇的克拉莉丝 题意: 解法: 考虑暴力解法,枚举起点的所有出边,拿边权乘以子树中蘑菇总数。最坏复杂度 \(O(nq)\) 。 考虑轻重链剖分,每个点只考虑连向父亲的边的贡献,连向重儿子的边的贡献,所有轻儿子的贡献我们用一个tag记录下来。连向父亲的边贡献和连向重儿子的边都可以直接计算。 下面考虑所有轻儿子的贡献。修改点u的蘑菇数量,只可能对他的所有祖先结点的轻链答案有影响(对于一个点v不是u的祖先,那么u对v的贡献一定在指向父亲的边里计算了)。 那么我们从u向上跳重链,对轻链到达的所有点打上贡献标记即可,这样每次修改复杂度 \(O(logn)\) 。 我们通过线段树维护子树内蘑菇大小就可以在 \(O(logn)\) 的复杂度内求出连向父亲和重儿子的边的贡献。 #include <bits/stdc++.h> #define ll long long using namespace std; const int maxn = 1e6; int n; ll bit[maxn + 11]; int son[maxn + 11],siz[maxn + 11],id[maxn + 11],top[maxn + 11],fa[maxn + 11],weight[maxn + 11]; vector

树链剖分 https://www.luogu.com.cn/problem/P3384

你说的曾经没有我的故事 提交于 2020-02-07 15:14:25
#include <cstring> #include <cstdio> #include <algorithm> #include <cmath> #include <cstdlib> #include <vector> #include <iostream> #define lson rt<<1 #define rson rt<<1|1 using namespace std; typedef long long ll; const int maxn=2e5+10; int N,M,R,P; int deep[maxn],F[maxn],size_[maxn],son[maxn];//深度,父亲节点,子树大小,重儿子 vector<int> G[maxn]; void dfs1(int x,int f) { int son_zhong=0,son_num=0; size_[x]=1; for(int i=0;i<G[x].size();i++) { int v=G[x][i]; if(v==f) continue; F[v]=x; deep[v]=deep[x]+1; dfs1(v, x); size_[x]+=size_[v]; if(son_num<size_[v]) { son_num=size_[v]; son_zhong=v; } } son[x]=son_zhong

P3950 部落冲突 树链剖分

痴心易碎 提交于 2020-02-04 11:08:09
题目链接 其实还是比较好做的,树链剖分现在越来越熟练了。对于这道题来说,所有的部落一开始全部在停战状态,所以它们的边权先初始赋为0,对于一个开战操作,就把链接这两个点的边上的边权赋一个大于0的数,查询时若是有和大于0的时候,我们便认为不能走。如果U停战操作,我们在C的时候要注意保存每一次开战的顺序,开展的村庄编号,停战的时候便将它们之间的边权赋为0。 代码如下: 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e6+7; 4 const int INF=0x3f3f3f3f; 5 struct node{ 6 int nxt,to,val; 7 }edge[maxn*3]; 8 struct node1{ 9 int l,r,sum,lazy; 10 }tree[maxn*4]; 11 struct node2{ 12 int u,v; 13 }st[maxn*2]; 14 int head[maxn],cnt; 15 void add(int x,int y){ 16 edge[++cnt].nxt=head[x]; 17 edge[cnt].to=y; 18 edge[cnt].val=0; 19 head[x]=cnt; 20 } 21 int n,m,kkk,x,y; 22 int q1