重心

树的重心

女生的网名这么多〃 提交于 2019-12-03 21:33:56
定义来源 https://oi-wiki.org/graph/tree-misc/ 树的重心 定义 以树的重心为根时,所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。 找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心。 删去重心后,生成的多棵树尽可能平衡。 性质 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。 把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两个树的重心的路径上。 在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。 求法:利用第二个性质 struct CenterTree { int n; int ans; int siz; int son[maxn]; void dfs(int u, int pa) { son[u] = 1; int res = 0; for (int i = head[u]; ~i; i = edges[i].next) { int v = edges[i].to; if (v == pa) continue; dfs(v, u); son[u] += son[v]; res = max(res, son[v]); } res = max(res, n - son[u]); if (res < siz) { ans = u;

@bzoj - 3162@ 独钓寒江雪

笑着哭i 提交于 2019-12-03 20:32:29
目录 @description@ @solution@ @accepted code@ @details@ @description@ 求一棵无根树上本质不同的独立集的个数 mod 10^9 + 7。 我们称两个独立集 A, B 是不同的,当前仅当: (1)存在一种方案,将树中的结点重新标号后,在 A 中出现的任意一条边在 B 中也应该出现。 (2)在满足条件(1)的前提下,以同样的重标号方式,如果 x 在 A 中属于独立集,在 B 中也应该属于独立集。 输入格式 第一行有一个正整数 n,表示结点数。结点从 1 编号到 n。 接下来 n - 1 行,每行两个整数 v, u,表示 v,u 两点之间有一条边。 输出格式 输出一行表示答案 mod 10^9 + 7。 样例输入1 1 样例输出1 2 样例输入2 6 1 2 1 3 1 4 4 5 4 6 样例输出2 9 数据范围与约定 对于 100% 的数据,n <= 500000。 @solution@ 如果没有本质相同的要求,直接树形 dp 就完事儿了。 问题说的是无根树,我们可以转成更方便的有根树来做(包括树形 dp 也是在有根树上做)。 记点 rt 为根。 如果在重新标号后 rt 的位置不变,那么依据题目所说,与 rt 相连的点依然与 rt 相连。也就是说,我们只是将 rt 的子树重排,并且只会把同构的子树互相换位置。

树相关算法(一)

匿名 (未验证) 提交于 2019-12-03 00:30:01
前言:算法竞赛中常见的树问题 (二叉)树的遍历 树的重心 树的直径 最近公共祖先(LCA) 哈夫曼树 树链剖分 一、(二叉)树的遍历 这种遍历顺序和DFS入栈的顺序很像,这种二叉树遍历方式称为先序遍历。除了先序遍历外,还有另外两种遍历,中序遍历和后序遍历。 先序遍历:访问根节点,遍历左子树,遍历右子树; 中序遍历:遍历左子树,访问根节点,遍历右子树; 后序遍历:遍历左子树,遍历右子树,访问根节点。 对于上面的那棵树,给出三种遍历方式是: 先序遍历: A->B->D->F ->G->H->I->E->C; 中序遍历:F->D->H->G->I->B->E->A->C; 后序遍历:F->H->I->G->D->E->B->C->A。 说了这么多,二叉树的遍历有什么用呢?答案是,没太大用。一般情况下是用来当做题目中的信息。对于OIer来说这些是常识,初赛会考的。 二、树的重心 树的重心,也叫树的质心。对于一棵树来说,删去该树的重心后,所有的子树的大小不会超过原树大小的二分之一。树的重心还有一个性质,是相对于树上的其他点而言的,就是删去重心后形成的所有子树中最大的一棵节点数最少。换句话说,就是删去重心后生成的多棵子树是最平衡的。一棵树的重心至多有两个。 #include<cstdio> #include<vector> #include<cstring> #include

# [Poj 3107] Godfather 链式前向星+树的重心

蓝咒 提交于 2019-11-29 22:01:51
[Poj 3107] Godfather 链式前向星+树的重心 题意 http://poj.org/problem?id=3107 给定一棵树,找到所有重心,升序输出,n<=50000。 链式前向星存储图 链式前向星是前向星的升级版本,是一种特殊的边集数组,有多少条边,数组就开多大,空间利用率高,并且速度比使用vector快,本题使用vector就TLE了一次。。 建立如下结构体: struct node{ int to,next,w; }edge[maxe] 其中edge[i].to表示第i条边的终点,edge[i].next表示与第i条边同起点的下一条边的存储位置(使用链式前向星存储遍历的时候是逆序遍历的,所有这里其实是前一条边的位置),edge[i].w表示第i条边的权值 inline void add(int u,int v,int w){ edge[cnt].w=w,edge[i].to=v; edge[cnt].next=head[u]; head[u]=cnt++; } 初始cnt=0,表示第几条边 其中head[u]表示以u为起点的第一条边的存储位置,实际上,由于head[u]的值会不断被覆盖,存储的实际上是最后一条边的位置,遍历的时候其实是逆序遍历的 head[]一般初始化为-1 遍历以u节点为起始位置的所有边,终止位置 i=-1 for(int i=head[u

IOI2019题解

為{幸葍}努か 提交于 2019-11-28 10:13:18
由于太懒了,好久没更新了。发个题解好了。 shoes 首先不难证明鞋子配对一定是从前往后将同一种的左和右配对。 配好对之后首先我们可以假设左在右的左边,然后讨论可知将左边靠前的排在前面更优。 rect 先考虑只有行限制的情况,那么我们考虑从大到小插入,那么每次极大的空区间就是符合题意的,这样就可以扣出O(n)个区间。 同时我们可以考虑矩形左上角,对于每个左上角考虑每个行区间的延伸长度,每个列区间的延伸长度,合并的时候是一个扫描线。 此外本题也有不带log的做法,留作习题。 split 假设a<=b<=c,那么显然有a<=n/3,b<=n/2。我们的目标就是要找到两个联通块,满足其中一个大小在[a,n-b](或在[b,n-a])。 我们对[a,n-b]和[b,n-a]都运行以下算法,简记为[l,r]。容易证明l+l<=r。 考虑dfs树。如果树上存在一条符合题意的割边,那么直接输出即可。 否则考虑重心,我们必然有去掉重心之后的每棵子树大小<l,否则这棵子树就符合题意(大小在[l,n/2]之间)。由于只有返祖边,我们只需考虑下属各联通块到重心的祖先是否有边。如果没有,那么它肯定和重心只能同属一个联通块(因为它的大小<l)。如果和重心只能同属一个联通块的大小和>r了那么无解,否则我们先把重心和重心的父亲断开形成两个子树,此时重心子树大小>r,然后我们把能连的子树一个一个抛弃重心往上面连

beta版本发布说明与总结

两盒软妹~` 提交于 2019-11-28 09:19:27
1.发布说明: 软件介绍: deta版本的发布最终是一个可安装使用的窗体程序,已经由Alpha版本的应用解决方案完成到一个程序; deta版本解决了Alpha版本遗留的软件技术方面错误问题,以及针对有关问题的优化,和个人体验后的优化; deta版本实现了软件的用户实际可用性的调整,即:软件的数据操作问题,放弃使用数据库,而直接操作Excel文件。 用户介绍: 设计考虑为:主要高校中有空余时间并且有意愿学习英语的群体;就实际情况而言,应该是能使用电脑的群体,可以在工作学习之余,用软件来学习英语单词。 2.软件安装使用说明: 一、软件安装: 点击“setup.exe”文件安装: 可自定义安装位置: 点击下一步,到安装完成。 二、软件使用: 可双击桌面快捷方式“RPdeta2.0.exe”,或者点击“开始”在点击“RPdeta2.0.exe”; 若遇到安全软件阻拦:请选择允许(本软件不会对你的电脑造成任何威胁)确认后即可使用 额外说明: 软件所有页面均可使用 【ESC】键返回,在主页可退出程序。 2.整体综合总结: 2.1开发设计阶段: 没有发挥团队各个队员“各尽所能”的作用: 考虑使用各个队员之间相互独立的开发相应的模块,并安排间隔时间进行交流沟通。 软件设计开发在团队之间没有重心: 由组长协调,分成结对小队,统筹安排整体软件的各个部分的开发设计。 软件设计与实际开发情况有特殊出入:

HDU 4812 (点分治)

廉价感情. 提交于 2019-11-27 10:25:15
题目: https://vjudge.net/contest/307753#problem/E 题意: 给你一颗树,树上每个点都有个权值,现在问你是否存在 一条路径的乘积 mod 1e6+3 等于 k的路径,如果有找到字典序最小的方案 思路, 树上路径~点分治 我们能知道每条路径的值,现在我们可以转化的问题是,怎么找一条路径等于K,和两条路径的乘积等于K, 首先第一种很明显就是判断相不相等即可,第二种的话,我们知道所有路径,我们怎么找到O(n)找到两个呢,我们用个数组存下所有是否出现过,然后,其实就是一个简单的小学问题,我们枚举每个距离的时候相当于 x,y,z已经知道 x,z了,式子是x*y=z,我们就只要判断z/x是否在标记数组中出现过即可,又因为这个有mod ,所以我们只能去乘z的逆元,这个时间卡的有点紧,我加了输入挂,和预处理逆元,map标记都不能用,只能用普通标记数组。 然后还有一个问题,你是否能和之前那样直接求出来所有的距离,答案是否定的,因为你直接去遍历数组标记,数组中的路径还含有两个都是同一子树的情况,这种时候是不能加入标记数组的,但是怎么避免呢,这里用到一个巧妙地方法,我们直接在计算所有路径到重心的距离的时候去更新答案,因为我们只有得到一个子树所有答案的时候才会存入标记数组,这样就避免一个子树的路径发生冲突的情况。最后我们再清空掉我们当前重心存入的答案。

洛谷 P3806 (点分治)

孤者浪人 提交于 2019-11-27 05:34:46
题目: https://www.luogu.org/problem/P3806 题意: 一棵树,下面有q个询问,问是否有距离为k的点对 思路: 牵扯到树上路径的题都是一般都是点分治,我们可以算出所有的路径长度然后保留下来,点分治无非就是几步一直递归,点分治就是在树上递归 1,找树的重心 2,算出所有点到重心距离,找出当前重心的所有合法路径 3,递归到子树 然后反复执行这三步 其实点分治唯一思考的地方就是 solve函数,其他都是一样的 https://www.cnblogs.com/guoshaoyang/p/10994997.html https://www.cnblogs.com/PinkRabbit/p/8593080.html #include<bits/stdc++.h> #define maxn 100005 #define mod 1000000007 using namespace std; typedef long long ll; vector<pair<ll,ll> > mp[maxn];//存下图 bool vis[maxn];//标记曾经使用过的重心 ll maxsize[maxn],dis[maxn],d[maxn];//maxsize 当前节点的最大子树 ll siz[maxn],e[maxn];// dis 到重心的距离 d 出现过的距离 ll n