虽迟但到
#1.树上问题
这东西一直是我的弱项所以从它开始。
1.树的直径
指树上最长的链。
首先随便找个点DFS找到距离这个点最远的点,然后以这个点为起点DFS找对距离它最远的点,这两个点之间的路径就是树的直径
code:
const int N = 10000 + 10;
int n, c, d[N];
vector<int> E[N];
void dfs(int u, int fa) {
for (int v : E[u]) {
if (v == fa) continue;
d[v] = d[u] + 1;
if (d[v] > d[c]) c = v;
dfs(v, u);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d %d", &u, &v);
E[u].push_back(v), E[v].push_back(u);
}
dfs(1, 0);
d[c] = 0, dfs(c, 0);
printf("%d\n", d[c]);
return 0;
}
2.树的重心
最大子树的size最小的点(无根树)
性质:
1.所有点到重心的距离和最小
2.一棵树以重心为根的时候其子树的大小都不超过整棵树的一半(判定重心的充要条件)
3.如果把两颗树通过一条边连起来形成新的树,那么新树的重心在原树两重心的路径上
4.树的重心不唯一,如果有两个重心那么他们一定相邻
5.一棵树任意定根,那么他的重心一定在其重链上
求法:
dfs求出最大子树的大小(无根树意义下),然后找到那个点
code:
void dfs(int now,int fa)
{
size[now] = 1;
int mm = 0;
for(int i = head[now];i;i = e[i].next)
{
int v = e[i].v;if(v == fa)continue;
dfs(v,now);
size[u] += size[v];
mm = max(mm,size[v]);
}
mm = max(mm,n-size[u]);
if(mx > mm)
{
mx = mm;
heart = now;
}
}
3.奇环树
1.处理方法
分为对环的处理和对树的处理,可以把奇环树看作森林,每棵树的根节点连成了环,然后对每个环上的点的子树进行处理最后合并,一般就是先找环然后树形dp了
2.从奇环树的直径开始讲起
按照上面的思路来写的话就是对于环上每个点的子树求一次树的直径,然后分两种情况讨论(下面经不经过环指奇环树的直径)
- 1.不经过环
那么此时的答案显然就是所有根节点对应子树的树的直径的最大值
- 2.经过环
那么我们已经处理出了每个子树离根节点最远的点,只要考虑环上的合并了,答案也很显然就是 a n s = max ( d p i + d p j + d i s i , j ) ans=\max(dp_i+dp_j+dis_{i,j}) ans=max(dpi+dpj+disi,j),然后对于环上的点的距离做一个前缀和就变成了 a n s = max ( d p i + d p j + d i s i − d i s j ) = max ( ( d p i + d i s i ) + ( d p j − d i s j ) ) ans=\max(dp_i+dp_j+dis_i-dis_j)=\max((dp_i+dis_i)+(dp_j-dis_j)) ans