lca

P5024 保卫王国[倍增+dp]

▼魔方 西西 提交于 2019-12-03 02:00:25
窝当然不会ddp啦,要写这题当然是考虑优化裸dp啦,但是这题非常麻烦,于是变成了黑题。 首先,这个是没有上司的舞会模型,求图的带权最大独立集。 不考虑国王的限制条件,有 \[ dp[x][0]+=dp[y][1]\\ dp[x][1]+=min(dp[y][1],dp[y][0]) \] 现在考虑限制条件,如果对每一个限制条件都做一次dp,复杂度达到 \(O(n^2)\) ,无法承受。 显然,对于这些限制条件,每一次的变动不会影响其它大多数的状态。 对于一个限制条件,我们分开考虑,先考虑只对一个城市进行限制的情况。 若该城市被要求驻扎军队,那么如果在最优情况下它本来就需要军队,则没有影响;如果它本来不需要军队,由于此时所有子树都是最优解,那么 它只会对从它到根节点的路径的最优解产生影响 。 若该城市被要求不能驻扎,那么与上面的情况类似,如果它本来需要驻扎,那么会对它到根节点的路径造成影响。 综上所述,我们是否可以考虑对于每个限制条件,只对两个点到根节点之间的路径的某些状态进行修改呢?答案是肯定的。 假设待修改节点为 \(x\) 。首先,此时除了 \(x\sim root\) 的路径上的节点所表示的状态,其它状态都是最优的。对于这条路径的更新,实际上就是又对这条路径做了一次dp,并强制 \(x\) 选或不选,从而限制转移。 显然这是可行的,但是复杂度仍为 \(O(n^2)\)

LCA of Binary Tree Issue

匿名 (未验证) 提交于 2019-12-03 01:19:01
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试): 问题: I have written a code for finding least common ancestor of nodes in binary tree but it seems to return null instead of the LCA. My algorithm is as below. Recursively find the ancestor in left and right branch of tree A node where left as well as right tree has matching elements in the LCA. public class LCA { public static BinaryTreeNode findLCA( BinaryTreeNode root, BinaryTreeNode node1 , BinaryTreeNode node2) { if (root == null) { return null; } BinaryTreeNode left = root.getLeft(); BinaryTreeNode right = root.getRight(); if (left != null &

BZOJ.5404.party(树链剖分 bitset Hall定理)

匿名 (未验证) 提交于 2019-12-03 00:41:02
题目链接 只有指向父节点的单向道路,所以c个人肯定在LCA处汇合。那么就成了有c条到LCA的路径,求最大的x,满足能从c条路径中各选出x个数,且它们不同。 先要维护一条路径的数的种类数,可以树剖+每条链维护一个bitset解决。用vector一条链加一个bitset,SDOI R2现场测过我记得空间还不算特别大。。当然本题数字只有1000种,一个点开一个bitset没问题。最后合并时还要通过线段树。 假设答案是x,那么c个人都要从可选特产中不重复地选x个,把每个人拆成x个点就是一个二分图完备匹配。 由Hall定理,左边集合(c*x个点)任意一个子集与右边集合相邻的点数应不小于该子集大小。因为每个人的x个点的连边相同(复制了x次),所以对每个人只需判断x个都选的子集。 c很小,2^c枚举子集。与右边集合相邻点数就是选中人的bitset的并的大小size。设枚举了s个人,那么每次枚举有 \(x*s \leq size\) 。 所以 \(x = \min\{\frac{size}{s}\}\) 。 好慢啊。。垫底了。。 学了下fwrite,然并软。 //220140kb 7192ms #include <cstdio> #include <cctype> #include <bitset> #include <algorithm> //#define gc() getchar()

LCA 树链剖分

匿名 (未验证) 提交于 2019-12-03 00:40:02
// LCA // 树链剖分 在线 #include<iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> using namespace std; int n,m,root,cnt,head[ 500001 ]; int hvyson[ 500001 ],fa[ 500001 ],siz[ 500001 ],dep[ 500001 ],top[ 500001 ]; struct uio{ int to,next; }edge[ 1000001 ]; void add( int x, int y) { edge[ ++cnt].next= head[x]; edge[cnt].to = y; head[x] = cnt; } void dfs1( int x, int f, int depth) { dep[x] = depth; fa[x] = f; siz[x] = 1 ; int maxson=- 1 ; for ( int i=head[x];i;i= edge[i].next) { int y= edge[i].to; if (y== f) continue ; dfs1(y,x,depth + 1 ); siz[x]

杂题

匿名 (未验证) 提交于 2019-12-03 00:22:01
1 一个多边形与一个圆求交 三角剖分 多边形每一个顶点向圆心连线,计算每一个三角形与圆面积交。 问题转化为三角形。 分五种情况讨论 2 铁人三项 设两段占比例分别为x,y, 列一堆方程。 3 询问多边形内可以放置的最大的圆。 二分,每条边往里缩,半平面。 4 三角形面积并 扫描线 每一段是一些梯形和三角形,利用梯形面积公式 simpson积分 圆的面积并 ( 4 f ( m i d ) + f ( L ) + f ( R ) ) / 6 ( 4 f ( m i d ) + f ( L ) + f ( R ) ) / 6 5 抛物线 切一刀 求面积 一个结论:面积是与切点三角形面积的三分之四 6 扫描线 平面上一些不相交的图形构成一个树形结构 把每个圆拆成上下两个点 扫描线 扔进set里 7 三维差积 五个点的三维凸包 4514 1 n n 个点的树,边有边权,把其中 k k 个点涂成黑色,其余的点涂成白色,最大化:每两个黑点间距离和+每两个白点间的距离和。 直接设 f [ i ] [ j ] f [ i ] [ j ] 表示 i i 子树里选 j j 个黑点的最大值。然后发现不能转移… 考虑到每条边是独立的,因此这样设状态:令 f [ i ] [ j ] f [ i ] [ j ] 表示 i i 的子树里选 j j 个黑点最多对答案有多少贡献。 一条边对答案的贡献就是 权值 × ×

[SDOI2013]森林,洛谷P3302,主席树+启发式合并

匿名 (未验证) 提交于 2019-12-03 00:22:01
正题 一看到题目,就令人窒息。。。 森林 森林,也就是说一开始有很多棵树。然后要查找路径第k大。 明显要找lca,就想到了树链剖分。每次往上跳把当前这一段记录下来,很明显要开n棵前缀主席树。 然后再让找出来的op个区间相减(right-(left-1)),变成op个区间和op个区间相减。所以往下跳即可。 问题就是加边要重建,而且你不知道你之前用过哪些编号。但是莫名水到30emm。 而且时间复杂度也承担不起。 想到一种更令人窒息的做法。 打破思维格局,每个点继承他的父亲的原有子树,并修改当前点权值所在的链。就是说,当前主席书维护的是根到i点的信息。 那么找答案也是十分迅捷的,x到y就可以直接用x+y-lca(x,y)-fa(lca(x,y))四棵主席树往下跳即可。 Ϊʲô? 像上面这棵丑陋的树。x点所在的主席树记录的是蓝色三角形的信息,y点记录的是绿色所在点的信息,那么减去lca所记录的信息和lca的父亲所在点所记录的信息就可以得出来橙色线(路径)的信息啦! 所以我们每次把这个点(lca)找出来(倍增),然后在求解即可。 对于连边这个东西,我们会想到一种神奇的加快合并的方法――启发式合并,那么我们每次用带权并查集记录一下当前子树的大小即可,重新构图(不怕emm)。 代码<有两份,另外一份太丑陋> #include <cstdio> #include <cstdlib>

树剖求LCA

匿名 (未验证) 提交于 2019-12-03 00:17:01
Orz #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define maxn 500010 using namespace std; struct node { int ed,nxt; }; node edge[maxn<<1]; int n,m,first[maxn],cnt,root; int dep[maxn],fa[maxn],top[maxn],rnk[maxn],son[maxn],siz[maxn],rnk_cnt; inline void add_edge(int s,int e) { ++cnt; edge[cnt].ed=e; edge[cnt].nxt=first[s]; first[s]=cnt; return; } inline void dfs_1(int now,int pre) { dep[now]=dep[pre]+1; fa[now]=pre; siz[now]=1; for(register int i=first[now];i;i=edge[i].nxt) { int e=edge[i].ed; if(e==fa[now]) continue; dfs_1(e,now); siz[now]+=siz[e]; if(son[now]==-1|

[LNOI2014]LCA

匿名 (未验证) 提交于 2019-12-03 00:17:01
Problem q个询问 给出 \(l,r,z\) 求 \(\sum_{i=l}^{r} dep[Lca(i,z)]\) \(n,q \leq 5*10^4\) 这显然是不太可做的 如果你要用 \(nq \log n\) 的做法 考虑离线。 把俩询问拆成 [ \(1,l-1\) ] , [ \(1,r\) ] 求出来的结果自然是 [ \(1,r\) ] - [ \(1,l-1\) ] 没有固定的点?怎么办? 我们想。假设当前询问点是 z 选到 x 这个点 ( \(l \leq x \leq r\) ) 那么 z 和 x 的 LCA 一定在 x 和 1 的路径上 所以每次 链上修改 把 x -> 1 的这条链整体加1 查询的时候 查询 1 ~ z 的链上的值就可以了。 至于链上整体加1 以及链上查询 可以用树链剖分+线段树解决。 如果没能理解 很抱歉我菜的可怜只能放一张 比较特殊的 图 让读者自行理解了。。 #include<bits/stdc++.h> using namespace std ; const int MAXN = 5e4 + 10 ; struct Query { int z , flg , id ; } ; vector < Query > v[MAXN] ; int n , Q ; struct Edge { int v , nxt ; } e[MAXN <<

【刷题】【LCA】跳跳棋

匿名 (未验证) 提交于 2019-12-03 00:15:02
(直接复制自luogu题解) 精巧的建模题。 划重点了划重点了一次只允许跳过1颗棋子,这句话是解题的关键。 手玩一下跳法,现有描述位置的递增三元组 (x,y,z) ( x , y , z ),研究它能够在一步之内跳到何处。 首先,中间的元素可以随意往两边跳到达状态 (2x-y,x,z) ( 2 x y , x , z )和状态 (x,z,2z-y) ( x , z , 2 z y ),注意到这两个三元组的边界是扩大了的。 对于两边的元素,设 d1=y-x,d2=z-y d 1 = y x , d 2 = z y 若 d1>d2 d 1 > d 2,则 c c可以往内跳,到达状态 (x,b-d2,b) ( x , b d 2 , b ) 若 d2>d1 d 2 > d 1,同理。 注意到这次到达的状态三元组的边界是缩小了的,且跳法具有唯一性 若 d1=d2 d 1 = d 2,则边界没法缩小了,假定为边界条件。 对缩小边界的跳法具有唯一性这一性质,我们可以联想到什么呢? 将初始状态和目标状态同时缩小边界,看能否产生交集。 用树来描述这一个状态集合(树父亲的唯一性对应缩小边界的唯一性)。 到这里40分就拿到了。 但是我们发现,树的状态太多,无法存储。 只能每次在线询问需要的状态,复杂度为 O(d) O ( d ), d d的两个节点的相对深度。 感觉这样就像裸奔,所以

树上倍增求LCA(RMQ)

匿名 (未验证) 提交于 2019-12-03 00:13:02
板子(反正我只是个垃圾) //树上倍增求LCA(RMQ),其实就是个简单的二进制拆分问题 //正确性很容易证明,不证了 //思想:eg: 求x节点和y节点的Lca,比较 x 与 y 节点的深度,将树中深的节点跳到 //与浅的节点相同深度(由大到小循环),如果刚好为深度浅的节点,那么就找到了,否则一起向 //上跳,找到最小的j使得他们所跳的位置不同,但父节点是相同的,返回所跳位置的父节点就好了 #include<bits/stdc++.h>//板子 using namespace std; const int maxn = 1000010; int head[maxn]; int deep[maxn];//节点深度 struct{ int v,net; }edge[maxn];//链式前向星(ORZ,向大佬低头) int n,m,root,cnt; int f[maxn][21];//每个当前节点的第2 ^ j次方是哪个节点 inline void add_edge(int x,int y) { edge[cnt].v = y; edge[cnt].net = head[x]; head[x] = cnt++; }//加边 void dfs(int cur) { for(int i=head[cur];i!=-1;i=edge[i].net) { if(!deep[edge[i].v