lca

[bzoj2286][Sdoi2011]消耗战

自作多情 提交于 2019-12-03 14:58:15
题目链接 如果对于每个询问跑一次$dp$,那么$dp[i]$为断开$i$这棵子树的最小花费。 这样的复杂度为$O(n*m)$,过于臃肿。 所以我们要对于每次询问降低这次询问的复杂度。 我们可以发现$m$个关键点,最多有$m-1$个$lca$。 简单证明一下,如果有两个点,会有$1$个$lca$点,如果有三个点,则第三个点会和上一个$lca$产生一个$lca$。 所以以这$2*m-1$个点构建一棵树,在这个树上跑$dp$ 虚树的构建推荐一个 巨巨的博客 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 250010; 5 const ll inf = 2e18 + 10; 6 struct node { 7 int s, e, next; 8 ll w; 9 }edge[maxn * 2]; 10 int head[maxn], len; 11 void init() { 12 memset(head, -1, sizeof(head)); 13 len = 0; 14 } 15 void add(int s, int e, ll w) { 16 edge[len] = { s,e,head[s],w }; 17 head[s] = len++;

P3398 仓鼠找sugar[LCA]

筅森魡賤 提交于 2019-12-03 14:41:15
题目描述 小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n。地下洞穴是一个树形结构。这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而他的基友同时要从他的卧室(c)到图书馆(d)。他们都会走最短路径。现在小仓鼠希望知道,有没有可能在某个地方,可以碰到他的基友? 小仓鼠那么弱,还要天天被zzq大爷虐,请你快来救救他吧! 解析 当然可以树剖。 一开始想用路径长作为判断依据,但总是WA,下数据发现就错那么一个两个小问,也是很玄学。。。 于是转而研究点如何作为判断依据。 对于一个这样的树链,它的两端点为 \(a,b\) ,如下图。 反过来想,如果我们要构造一条路径,使得树上某一个点到另一点的路径与现有路径相交,该如何做呢? 首先,这个点肯定要先有一部分路径连到原先的树链上吧,否则不可能相交。 构造出的路径剩下的部分只可能是这三种情况。 而如果这样构造路径就违反了树的定义。 我们发现, 构造出的路径一定有一个点在原树链上 。但是这样还是不好下手,我们并不知道如何寻找这个点。 再进一步观察,发现新路径两端点的lca一定在原树链上。而lca很容易求,爱怎么求怎么求。 因此对于原问题,我们只需要判断某一对点的lca是否在另一对点表示的树链上即可。 判断一个点是否在一条树链上很容易,如果有一个点 \(x\) ,我们要判断它是否在 \(a,b\) 构成的树链 \(

NOIP2018 保卫王国

↘锁芯ラ 提交于 2019-12-03 14:15:07
题目 Z 国有 \(n\) 座城市, \(n−1\) 条双向道路,每条双向道路连接两座城市,且任意两座城市都能通过若干条道路相互到达。 Z 国的国防部长小 Z 要在城市中驻扎军队。驻扎军队需要满足如下几个条件: 一座城市可以驻扎一支军队,也可以不驻扎军队。 由道路直接连接的两座城市中至少要有一座城市驻扎军队。 在城市里驻扎军队会产生花费,在编号为 \(i\) 的城市中驻扎军队的花费是 \(p_i\) 。 小 Z 很快就规划出了一种驻扎军队的方案,使总花费最小。但是国王又给小 Z 提出了 \(m\) 个要求,每个要求规定了其中两座城市是否驻扎军队。小 Z 需要针对每个要求逐一给出回答。具体而言,如果国王提出的第 \(j\) 个要求能够满足上述驻扎条件(不需要考虑第 \(j\) 个要求之外的其它要求),则需要给出在此要求前提下驻扎军队的最小开销。如果国王提出的第 \(j\) 个要求无法满足,则需要输出 \(-1\) 。现在请你来帮助小 Z。 思路1 倍增 首先,这题给的切分非常多,所以考场上稳妥的策略应该是去打切分而不是正解。 正解有很多写法,这里写一种我认为最正常的写法:倍增。 首先得到两个数组: \(f[x][0/1]\) 表示在 \(x\) 这个子树,选或不选 \(x\) 点的最优方案。 \(g[x][0/1]\) 表示对于全局而言,选或不选 \(x\) 的最优方案。

[总结]最近公共祖先(倍增求LCA)

前提是你 提交于 2019-12-03 13:32:09
目录 一、定义 二、LCA的实现流程 1. 预处理 2. 计算LCA 三、例题 例1: P3379 【模板】最近公共祖先(LCA) 四、树上差分 1. 边差分 2. 点差分 3. 例题 一、定义 给定一颗有根树,若节点z既是节点x的祖先,也是节点y的祖先,则称z是x,y的公共祖先。在x,y的祖先中,深度最大的一个节点称为x,y的最近公共祖先(Least Common Ancestors),记做LCA。 如图:LCA(5,7)=2;LCA(3,8)=1;LCA(6,10)=6。 二、LCA的实现流程 LCA一共有三种可以实现的方法: 向上标记法 倍增法 Tarjan算法(方法一的优化) 当然我们最熟悉不过的还是倍增法求LCA。 1. 预处理 在引入倍增优化之前,我们先来看看寻找两点LCA的朴素做法。 以寻找节点5,节点2的LCA为例: 首先求出每个点的深度,dep[7]=4,dep[5]=3。 我们先从深度大的节点7开始向上跳,直到深度和5一致,即跳到了节点4,此时这两个节点还没有到同一个点。 接下来我们继续让节点4和节点5向上跳,当他们跳到节点2的时候,由于跳到了相同节点,因此确定节点2是节点7,节点5的LCA。 显然,对于这样暴力的做法,速度慢的原因在于每一次只向上方跳一步,想要加快向上跳的速度,就需要采用倍增法进行优化。 我们设fa[x,k]表示x向上跳 \(2^k\)

树链剖分与倍增求LCA

风格不统一 提交于 2019-12-03 11:57:45
树链剖分与倍增求 \(LCA\) 首先我要吐槽机房的辣基供电情况,我之前写了一上午,马上就要完成的时候突然停电,然后 \(GG\) 成了送链剖分 其次,我没歧视 \(tarjan LCA\) 1.倍增求 \(LCA\) 理解较为简单的一种方法,但速度略慢 倍增是啥? 每个数字都可以拆成几个二的整数次的和,我们可以找出每个数字是由哪几个二的整数次的数合成的 比如说 \(14 _ {10} = 1110_2 = 1000 _2 + 100 _2 + 10 _2 = 8 _ {10} + 4 _{10} + 2 _{10}\) 那么我们如果要统计一段长度为十四的区间的最小值,我们就可以先统计前八个数的最小值,再统计之后的四个,再统计之后的两个。 我们可以用 \(f[i][j]\) 表示从 \(i\) 开始往后 \(2^j\) 长度的最小值 给宁康康代码 for( int i = 1; i <= 23; i++ ){ for( rint j = 1; j <= n; j++ ){ //a[i][j]存的是i往后2的j次长度的区间的右节点是哪儿 f[i][j] = min( f[i][j - 1], f[a[i][j - 1]][j - 1] ); } } 下面这个东西是啥意思呢 f[i][j - 1], f[a[i][j - 1]][j - 1] \(2^j = 2^{j-1} +2^{j

天天爱跑步

泪湿孤枕 提交于 2019-12-03 11:57:11
天天爱跑步 一道:树上差分+LCA+桶的题 说实话,这道题放在D1T2就是非常不合理的。然而CCF就是放了,并且还是能依靠CSP捞钱,你也必须交钱参加比赛。这个社会是多么的不公啊! 闲扯结束 显然如果对每条路径都进行一次处理,复杂度不对。考虑对路径进行一次预处理,然后进行统一的计算答案。我们发现当一条路径对某一个点P产生贡献时满足这个条件: 1.当 \(P\) 在该路径的 \(S\) 与 \(LCA\) 之间时 \(dep[S]-dep[P]=watch[P]\) 移一下项为 \(dep[S]=dep[P]+watch[P]\) 那么该路径起点对P有贡献 2.当 \(P\) 在该路径的 \(LCA\) 与 \(T\) 之间时 \(dist[S,T]-watch[P]=dep[T]-dep[P]\) 移一下项 \(dist[S,T]-dep[T]=watch[P]-dep[P]\) 那么该路径终点对P有贡献 然后用一个全局桶维护这些贡献 当枚举到当前点的时候 将当前点作为终点和起点的贡献加在桶里面。然后计算当前点的ans 细节:一个它只会受其子树的贡献,其他的贡献不会有的。如何处理?在开始遍历这个点的时候用 \(ad1,ad2\) 来存储已经存在的桶里面的值 然后遍历子树 当又返回到当前节点的时候 计算当前节点的答案。显然当前节点的答案为 \(ans[x]+=bas1[watch[x

NOIP2018 D2T3 保卫王国

懵懂的女人 提交于 2019-12-03 11:21:55
学习自: @秦淮岸 Force 如果没有限制条件,那么这道题就跟战略游戏 \(or\) 没有上司的舞会是一样的,只需 \(dp\) 一次就够了,所以很容易想到一个 \(44pts\) 的暴力:对于每次询问,都跑一遍 \(dp\) ,其中让 \(a, b\) 两个点强制转移(放/放)即可。 Thoughts 无解情况 显然,一般来说都是有解的,除非: 命令冲突,即 \(a = b\) ,且 \(x \not= y\) \((a, b)\) 有一条边,即存在父子关系,而两个都不能放,即 \(x = 0 \& y = 0\) 分部分 发现若用暴力,很多地方的 \(dp\) 显然都是相同的。 这道题核心是: 相邻两个点上必须有一个点有守卫 那么我们考虑把每次询问,将整张图分成若干个 好预处理 的部分,每个部分 分开处理 ,让每个部分内部符合条件,让 每个部分交界处满足条件 。因为每个部分互不干扰,所以可以相对取 \(min\) 最优解。 考虑简单的情况, \((a, b)\) 在一条链上: PS: \((a, b)\) 是对称的,如果 \(a\) 在底下咱们 \(swap\) 一下就行了 那么如果 \((a, b)\) 不在一条链上,我们可以把它看做两条链: \(a\) 到 \(lca\) \(lca\) 到 \(b\) 具体情况如下图 所以,一切 \((a, b)\)

csp-s模拟51

蓝咒 提交于 2019-12-03 10:27:32
T1: 树剖,分情况讨论当前root与操作节点的位置关系 1.u,v的lca -> lca(u,v),lca(u,root),lca(v,root)中dep最大的点 2. \(tree_u\) 的权值和 -> 若u不在1-root的链上, \(siz_u\) 。否则,v是u到root链上u下方的第一个点, \(siz_1-siz_v\) 特别的,root的ans为 \(siz_1\) T2: 考虑实际含义: 每个格子有一个权值,每一列格子的权值都是相同的。 从一个起点开始,每次可以向上走一格或者向左上角走一格,直到走到最上面一行为止。 你需要最小化经过的格子的总权值。 首先最优路径显然是先斜着走到较小的那一列,然后再一直向上走 所以我们设 \(g_y(i,x)\) 表示考虑到第y列,从(x,y)开始斜着走到i列再顶到头的代价 \(t_y(x)\) 表示表示考虑到第y列,从(x,y)开始走到顶的最小代价 则 \(g_y(i,x) = sum_{i,y} + (x-(y-i+1))*a[i]\) \(t_y(x)=min_{i=1}^y{g_y(i,x)}\) 发现当y一定时,g函数是一个关于x的一次函数,而t函数则是一个由g拼成的凸包 而当y增大时,g函数只有截距会变化 所以可以将询问离线下来,从大到小枚举y,每次插入一个一次函数,维护凸包即可。 T3: 生成函数+FFT 咕了 来源

树上差分学习笔记

可紊 提交于 2019-12-03 10:03:23
学习链接: https://www.sohu.com/a/271430685_100201031 树上差分的方式有两种: 一.点差分:改变树上路径(u,v)上的所有的点的点权值,假设增加了val值 我们对每一个节点维护一个tag数组作为差分数组 考虑改变的影响? tag[u]+=val,tag[v]+=val,tag[lca(u,v)]-=val,tag[fa(lca(u,v))]-=val; 同一条路径上,我们改变端点的权值,这样分别从u,v回溯的时候,我们就可以求出这条路径上所有点的权值了! 然后考虑将fa[lca(u,v)]的权值减掉val,因为我们对链(u,v)的操作不应该对链外节点造成影响! 一般可以用倍增求LCA的方式处理 例题: 1.洛谷:P3128 [USACO15DEC]最大流Max Flow CODE: /* */ #include<cstdio> using namespace std; const int N=500020,logN=19; struct node{ int from; int to; int next; }e[N<<1]; int head[N],num,n,m,log[N],f[N][logN],dep[N],tag[N],ans; inline int max(int x,int y) { return x>y?x:y; } void

LCA 倍增

旧时模样 提交于 2019-12-03 04:54:21
HDU 2586 #include<bits/stdc++.h> using namespace std; const int maxn=4e4+4; const int maxbit=15; struct edge{ int to; int val; // edge(int a,int b) // { // to=a; // val=b; // } }; int father[maxn][maxbit]; int dep[maxn]; int dis[maxn]; int lg[maxn]; vector<edge> g[maxn]; void dfs(int nowp,int fa) { dep[nowp]=dep[fa]+1; father[nowp][0]=fa; for(int j=1;j<=lg[dep[nowp]]+1;++j) { father[nowp][j]=father[father[nowp][j-1]][j-1]; } for(int i=0;i<g[nowp].size();++i) { edge &e=g[nowp][i]; if(e.to!=fa) { dis[e.to]=dis[nowp]+e.val; dfs(e.to,nowp); } } } int lca(int u,int v) { if(dep[u]<dep[v]) swap(u,v);