C语言 | 线段树】的更多相关文章

题目传送门 https://loj.ac/problem/3046 题解 首先问题就是问有多少条路径是给定的几条路径中的一条的一个子段. 先考虑链的做法. 枚举右端点 \(i\),那么求出 \(j\) 表示经过 \(i\) 的路径,左端点最小是 \(j\),那么右端点 \(i\) 的贡献就是 \(i-j+1\). 至于求出 \(j\) 可以用直接线性地从右向左扫一遍,在右端点处枚举路径就可以了. 那么问题回到树上. 我们考虑也枚举最终的路径的一个端点. 那么,这个端点的贡献,应该就是经过这个端点…
#include<stdio.h> #define MAX_LEN 1000 void build_tree(int arr[],int tree[],int node,int start,int end) { /* int arr[]: y int tree[]: int node:树的根节点 int start:arr数组的 int end:arr数组的 */ if(start==end) { tree[node] = arr[start]; } else { int mid = (sta…
原文链接www.cnblogs.com/zhouzhendong/p/UOJ470.html 前言 做完情报中心来看这个题突然发现两题有相似之处然后就会做了. 题解 首先,我们考虑将所有答案点对分为两类. 一个节点对其祖先的贡献. 来自一个节点的不同子树之间节点的贡献. 第一种情况非常简单,这里不加赘述. 对于第二种情况,我们首先考虑简单做法: 考虑对于每一个节点分开处理. 按照某一种顺序枚举它的子树,对于所有"一端在当前子树内,另一端在当前子树之前的子树"的路径,我们求它们的贡献.…
题目大意 给定一棵$n$个节点的树,维护$n$个集合,一开始第$i$个集合只有节点$i$.有$m$个操作,每次操作输入一个$(u,v)$,表示将$(u,v)$这条链上所有点所属的集合合并.求有多少个无序数对$(u,v)$使得$u,v$同在一个集合之中. $$n,m\leq 10^5$$ 思路 其实这道题要维护的就是极大联通子集使得这个集合的节点都讲同一种语言(以下简称联通块)的合并和维护大小. 设$s_x$表示$x$所在的联通块的大小,$ans=\frac{1}{2}\sum_{x}s_x$.…
题意 题目链接 分析 考虑枚举每个点的答案,最后除以 2 即可. 可以与 \(u\) 构成合法点对 的集合 为所有经过了 \(u\) 的链的并.因为这些链两两有交,根据结论 "树上两条相交的链一定有一条的 \(lca\) 在另一条链上" 可以得知所有的链构成了一棵树. 考虑维护经过每个点的所有链构成的 树链的并 的大小.一条链是否出现可以树上差分,树链的并的具体大小就以 \(dfs\) 序 为下标建线段树,然后线段树合并即可. 复杂度 \(O(nlogn)\) . 代码 #includ…
首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案.根据实现不同有下面几种方法.三个log:假如对每个点都存下经过它的链并S[x],那么每新加一条路径进来的时候,相当于在路径上所有点的S中都加入这条路径.树剖之后,相当于对log个区间中的点都加入log个区间.具体实现有树剖后线段树维护虚树.矩形扫描线.线段树+set存区间等多种方法,这里不再多说.两个log:先树剖,然后对每个点开一棵线段树存储它的S,由于题中没有修改,所以可以树上差分+线段树合并,这样可以将方法一中…
暴力树剖做法显然,即使做到两个log也不那么优美. 考虑避免树剖做到一个log.那么容易想到树上差分,也即要对每个点统计所有经过他的路径产生的总贡献(显然就是所有这些路径端点所构成的斯坦纳树大小),并支持在一个log内插入删除合并. 考虑怎么求树上一些点所构成的斯坦纳树大小.由虚树的构造过程容易联想到,这就是按dfs序排序后这些点的深度之和-相邻点的lca的深度之和(首尾视作相邻),也就相当于按dfs序遍历所有要经过的点并回到原点的路径长度/2. 这个东西显然(应该)可以set启发式合并维护,但…
原题链接戳这儿 SOLUTION 考虑一种非常\(naive\)的统计方法,就是对于每一个点\(u\),我们维护它能到达的点集\(S_u\),最后答案就是\(\frac{\sum\limits_{i=1}^{n}|S_i|}{2}\) 也就是说我们可以先树剖一下,对于每一个点都开一棵线段树,每次修改\(O(nlogn)\)地更新一下路径上的线段树,最后查询一下就行了 但是这样的复杂度是\(O(n^2log^2n)\)的,显然会炸.注意到每次是对一条链上的所有点操作,所以我们可以查分.又因为差分之…
分析 问题显然可以转化为对于每个节点询问所有这个节点的所有链的链并的大小. 考场上我直接通过树剖打标记+树剖线段树维护以\(O(n \log^3 n)\)的时间复杂度暴力实现了这个过程.(使用LCT或者全局平衡二叉树可以实现\(O(n \log^2 n)\)的时间复杂度) 考虑如何快速求出链并的大小,有这样一个结论:把所有的链的端点按dfs序排序后,链并的大小等于所有链的两端点的深度之和减去相邻端点的LCA的深度之和再减去所有端点的LCA的深度,这个结论(貌似)在链并是一个连通块的时候均成立.…
/* 线段树学习:如果一个节点为i,那么他的左孩子为2I+1,右孩子为2i+2: */ #include<stdio.h> #define min(a,b) a<b?a:b; ]; ] = {,,,,,}; //创建树 void create(int root, int c[], int start, int end) {//root表示当前线段树下标 //a表示用来构造线段树的数组 //star表示数组起始位置 //end表示数组末尾位置 if (start == end) { //叶…