lca

poj3471 - 倍增+LCA+树上差分

匿名 (未验证) 提交于 2019-12-02 23:57:01
题意:一张n节点连通无向图,n-1条树边,m条非树边。若通过先删一条树边,再删一条非树边想操作 将此图划分为不连通的两部分,问有多少种方案。 利用LCA整好区间覆盖,dfs用来求前缀和 需要注意的是,覆盖数为1的时候才可以选择哦! 覆盖数为0,代表可以直接拆开 最后附上一张我老婆 #include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #define maxn 110000 using namespace std ; typedef long long ll ; int dp [ maxn ][ 33 ]; int dep [ maxn ]; long long cnt [ maxn ]; //差分数组 int head [ 450100 ]; struct Node { int to ; int next ; } G [ 450100 ]; int cnn = 1 ; void insert ( int be , int en ) { G [ cnn ]. to = en ; G [ cnn ]. next = head [ be ]; head [ be ] = cnn ;; //头插法 cnn ++; } void dfs ( int u , int par ) { dep [

模拟94 题解

被刻印的时光 ゝ 提交于 2019-12-02 23:52:41
A. 凉宫春日的忧郁 数据范围就长得很可写高精度的样子。 可以维护高精度的高位,舍弃低位信息。 正解是取对数。 $x^y=y*log\ x$ $y!=\sum \limits_{i=1}^{y}log\ i$ 然后可以直接比较两个取对之后的$double$类型。 B. 漫无止境的八月 显然将问题转化为差分。 单点修改转化为单点加一个值,下一位减去一个值。 区间加法转化为当前位加上一个值和$k$位后减去一个值。 然后发现可以把全部的信息都转移到最后$k$位,然后用一个变量维护$k$个数中有多少个不为$0$就完了。 然而本题其实是语文题,谁能想到出题人修改的是终止区间呢。(并不看样例解释) C. 射手座之日 显然区间$[i,j]$的$lca$为区间中的$dfs$序最小值和最大值的$lca$。 所以想到分治维护最值,然后这个$lca$就并不像方案数/乘法/加法等信息可以用结合律简单维护。 部分分提示我们:通过枚举$lca$,找出有多少个区间,满足最值在$lca$的$dfs$序范围内。 然而这个似乎挺难做的,然后就完戏了。 正解要继续这个思路: 因为直接维护区间很困难,不妨考虑插入子树内每一个值的过程。 将子树内的每一个值插入序列中,检查多少个序列可以形成了连续的区间。 这个过程可以用链表(通过记录左右端点模拟)简单维护。 为了保证复杂度,选择树上启发式合并。

[LNOI2014] LCA

匿名 (未验证) 提交于 2019-12-02 23:52:01
给定n个节点的有根树,q次询问,每次询问求$\sum _{l\leq i\leq r} dep[LCA(i,z)] $ ˼· 根据wys巨神所说 ,如果不把dep这个约束去掉,那么将不容易用数据结构来维护,因为对于不同的i, \(dep[LCA]\) 可能不一样 1.为了去掉dep,我们采取一种奇技淫巧,对于上面的一个 \(i\) ,将 \(root\) 到 \(i\) 这条路径上的所有点权加一,那么 \(dep[LCA]\) 就可以通过查询 \(root\) 到 \(z\) 这条路径上的点权和得到( WTF??? ) 于是问题变成了 链修改 与 链查询 ,可以请出我们 代码简短好写 的树链剖分同学 然而我们不能对每个询问都暴力修改 \(root\) 到 \([l,r]\) 这一些链,所以我们还需要一些小优化 2. 离线处理 ,将每个询问拆成 \([1,l-1]\) 和 \([1,r]\) 两份,那么这个询问可以用 \(ans[1,r]-ans[1,l-1]\) 得到,由于链修改操作与z无关(因为增加 \(root\) 到 \(i\) 所以只与 \(i\) 有关),所以可以对所有询问的右端点排序,然后for一遍依次链修改即可 树链剖分模板题 由于有很多重复的操作(+代码过丑)所以去掉不必要的部分啦~~~ Code: struct Q { int r,z,opt; ll ans;

倍增求LCA

匿名 (未验证) 提交于 2019-12-02 23:49:02
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cstdlib> 6 using namespace std; 7 #define maxn 500010 8 9 int n,m,s,tot; 10 int deep[maxn],first[maxn],fa[maxn][22]; 11 struct edge{ 12 int nextx,to; 13 }e[maxn<<1]; 14 15 void add(int u,int v) 16 { 17 tot++; 18 e[tot].to=v; 19 e[tot].nextx=first[u]; 20 first[u]=tot; 21 } 22 23 void dfs(int x,int f,int depth) 24 { 25 deep[x]=depth; 26 fa[x][0]=f; 27 for(int i=1;(1<<i)<=deep[x];i++)//注意此处应从1开始,i-1必须为正 28 fa[x][i]=fa[fa[x][i-1]][i-1]; 29 for(int i=first[x];i;i=e[i].nextx) 30 { 31 int u=e[i].to; 32 if(u=

[NOIP2016] 天天爱跑步

匿名 (未验证) 提交于 2019-12-02 23:49:02
$link$ $solution:$ 考虑二元组 $(S,T)$ 对 $u$ 点的贡献。 若 $S$ 在 $u$ 子树上 ( $T$ 不在),且满足 $dep_u+w_u=dep_S$ 就可以对 $u$ 作贡献。 若 $T$ 在 $u$ 子树上 ( $S$ 不在) ,且满足 $w_u-dep_u=dep_S-2\times dep_{lca}$ 就可以对 $u$ 作贡献。 所以只要将 $(S,T)$ 拆成 $(S,lca),(lca,T)$ 即可。 对于计算直接线段树合并与简单差分即可。 时间复杂度 $O(m\log m)$ #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=300001; struct node{ int u,v,nex; }x[MAXN<<1]; int n,m

关于求LCA三种方法

匿名 (未验证) 提交于 2019-12-02 23:49:02
1.倍增 可以理解为暴力的优化.先让两个点跳在同一高度,再一起跳2^k次倍祖先. 难点: 倍增数组转移方程:bz[i][j]=bz[bz[i][j-1]][j-1] BZCLCA 2.RMQ 提前说明这种算法比较慢,且很容易错.写在这里便于对RMQ的理解复习. 大概思路是建树后跑一遍dfs以求dfn,用数组记录点在dfn中的第一次出现的位置. LCA一定在这两个点的dfn区间内,用RMQ求区间深度最小即LCA.有一点说 明的是在构建RMQ时用数组用同样的方式记录最小值对应的点. RMQLCA 3.tarjan 提前说明为离线算法,复杂度O(n+q),较快.ps:q为询问次数. 大概思路为构建已经存在的树与询问树.先dfs遍历已经存在的树, 再从遍历的节点中寻找询问树中与其关联的节点.看询问树中的节点是否 遍历过,遍历过就一定走过了LCA,用并查集维护最开始的爸爸则是LCA. 其中疑问,最开始的爸爸不是根结点吗?不,根节点的儿子还为遍历回去,还未认这个爸爸. 以此类推,存在树中的所有子树结构均满足这中关系. TARJANLCA

LCA(最近公共祖先)暴力转倍增算法

匿名 (未验证) 提交于 2019-12-02 23:48:02
今天,南外夏令营第一天打卡 , 咳咳, 概念 :为了便于了解,如图(在一颗有根树中,有若干个子树):   其中1号节点为该树的根,我们所谓的祖先即一个子树的根及其的根的根和其的根的根的根……(在LCA中包括子树本身) (其实不用那么绕口) ,举个例子(在本文之后直接用编号来称呼节点): 5的祖先:5、2、1;7的祖先:7、3、1;2的祖先:2、1……   而所谓的最近公共祖先为两个节点 首先 能找到的公共祖先(即 深度最最大的公共祖先 )   来个 模版题 () 下面就是激动人心的代码时刻让我们进行代码实现的步骤:    1、 先从最简单的 暴搜 入手:通过链表(从根一直往下搜,标记父节点) nlogn ) struct st{ int to,next; }edge[]; int hd[]; void add(int from,int to) { cnt++; edge[cnt].next=hd[from]; edge[cnt].to=to; hd[from]=cnt; } void DFS(int now,int d)找祖先 { deep[now]=d; for (int i=hd[now];i;i=edge[i].next) { fa[edge[i].to]=now; DFS(edge[i].to,d+1); } } void LCA() { if (deep[x]<deep

[bzoj3331] [BeiJing2013] 压力(tarjan 点双连通分量)

匿名 (未验证) 提交于 2019-12-02 23:48:02
1 if(!dfn[to]){ 2 tarjan(to); 3 low[x]=min(low[x],low[to]); 4 if(low[to]>=dfn[x]){ 5 opt++; sum++; 6 if(x!=1||opt>1) cut[x]=1; 7 do{ 8 tmp=sta[up--]; 9 cir[tmp]=sum; 10 dcc[sum].push_back(tmp); 11 }while(tmp!=to); 12 cir[x]=sum; 13 dcc[sum].push_back(x); 14 } 15 } 16 else low[x]=min(low[x],dfn[to]); 1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<iostream> 5 #define $ 220010 6 using namespace std; 7 int m,n,q,first[$],tot1,dfn[$],low[$],tar,tip[$],second[$],tot2; 8 int dad[$][22],sta[$],up,ans[$],dep[$],sum,pre[$]; 9 struct tree{ int to,next; }a[$*5],tr[$*5]; 10 inline int

[NOIP2016]天天爱跑步 题解(树上差分) (码长炒鸡短还跑的贼快)

匿名 (未验证) 提交于 2019-12-02 23:47:01
Description 小c同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要 玩家每天按时上线,完成打卡任务。这个游戏的地图可以看作一一棵包含 N个结点和N-1 条边的树, 每条边连接两 个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从1到N的连续正整数。现在有个玩家,第个玩家的 不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以 每个人的路径是唯一的)小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点的观察员会选 每个观察员会观察到多少人?注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时 间后再被观察员观察到。 即对于把结点J作为终点的玩家: 若他在第Wj秒重到达终点,则在结点J的观察员不能观察 到该玩家;若他正好在第Wj秒到达终点,则在结点的观察员可以观察到这个玩家。 Input 第一行有两个整数N和M 。其中N代表树的结点数量, 同时也是观察员的数量, M代表玩家的数量。 接下来n-1 行每行两个整数U和V ,表示结点U 到结点V 有一条边。 接下来一行N 个整数,其中第个整数为Wj , 表示结点出现观察员的时间。 接下来 M行,每行两个整数Si和Ti,表示一个玩家的起点和终点。 对于所有的数据,保证 。

【BZOJ 4771】七彩树

匿名 (未验证) 提交于 2019-12-02 23:39:01
一直TLE的原因竟然是数组开太大了导致 \(memset\) 清空耗时超限,亏我还调了1天啊(T^T) 给定一颗树,每个节点都有一个颜色,要求多次询问某个节点 \(x\) 的子树中深度不超过 \(d\) 的节点中,有多少种不同的颜色,强制在线。 首先,我们可以考虑两个相同颜色的节点对答案的贡献。 很显然,他对他们自己所有的祖先的答案贡献都为 \(1\) ,在他们 \(lca\) 及以上的地方被重复计算了一次,所以说都需要减去 \(1\) ; 然后考虑对于相同颜色的节点,对相同颜色节点的 \(dfs\) 序维护一个set,每一次去寻找他的前趋与后继,进行我们刚刚抽离出来讨论的操作,这样的话就可以很好的解决这个问题。 那应该怎么维护呢?很显然,如果没有距离限制,在每个结点处维护一颗线段树足矣。如果有距离限制,受『谈笑风生』一题的启发,我们可以以深度来维护一颗主席树,这只需要我们在进行上一段讨论的操作时按照深度顺序进行操作,对原操作是毫无影响的。 具体见代码,常数略大: #include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; struct cc{ int to,nex; }e[maxn*2]; int head[maxn],tot; int a[maxn],b[maxn]; int ls[maxn*50]