牛客周赛 Round 104(小红的树不动点)
小红的树不动点
跟E题有类似当i为不动点时,1-i-1个数都必须在区间内,但此时是在树上的话就改为公共祖先相同,所以他是不动点的条件就为根节点就为他们的公共祖先,所以i成立的树就有1-i的lca的深度个。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <iostream>
#include <bits/stdc++.h>
#define ll long long
using namespace std;int n; // 树的节点数
vector<vector<int>> a; // 邻接表存储树结构
int shu[200005][18]; // 倍增表:shu[i][j]表示节点i的第2^j级祖先
int head[200005]; // head[i]存储节点i的深度(距根节点的距离)
int logn[200005]; // 对数表:logn[i] = floor(log2(i))
bool b[200005]; // DFS访问标记数组// 深度优先搜索初始化树结构
void dfs(int h, int k) {b[h] = true; // 标记当前节点已访问head[h] = k; // 记录当前节点的深度kfor (int i = 0; i < a[h].size(); i++) {if (!b[a[h][i]]) { // 遍历未访问的邻接节点shu[a[h][i]][0] = h; // 记录直接父节点(2^0级祖先)dfs(a[h][i], k + 1); // 递归访问子节点,深度+1}}
}// 预处理倍增表和log表
void chu() {// 初始化对数数组(避免重复计算log)logn[1] = 0;logn[2] = 1;for (int i = 3; i <= n; i++) {logn[i] = logn[i / 2] + 1; // 递推计算log2(i)的整数部分}// 构建倍增表(核心预处理)for (int j = 1; (n >> j) > 0; j++) { // j: 2^j不超过总节点数for (int i = 1; i <= n; i++) {if (shu[i][j - 1] != 0) { // 确保祖先存在// 倍增递推:2^j级祖先 = 2^{j-1}级祖先的2^{j-1}级祖先shu[i][j] = shu[shu[i][j - 1]][j - 1];}}}
}// 基于倍增法查询最近公共祖先(LCA)
int lca(int l, int r) {// 确保r是较深的节点[5,10](@ref)if (head[l] > head[r]) swap(l, r);// 将较深节点r提升到与l同深[9](@ref)int q = head[r] - head[l];while (q != 0) {r = shu[r][logn[q]]; // 按2的幂次跳跃[11](@ref)q -= 1 << logn[q]; // 更新剩余深度差}// 若提升后重合,直接返回[4](@ref)if (l == r) return l;// 同步向上跳跃至LCA下方[3,10](@ref)for (int j = (int)log2(n); j >= 0; j--) {if (shu[l][j] != shu[r][j]) { // 祖先不同才跳跃l = shu[l][j];r = shu[r][j];}}return shu[l][0]; // 返回最终公共祖先
}int main() {ios::sync_with_stdio(false); // 禁用C与C++流同步cin.tie(nullptr); // 解除cin与cout的绑定// 输入树结构cin >> n;a.resize(n + 1);int l, r;for (int i = 0; i < n - 1; i++) {cin >> l >> r;a[l].push_back(r); // 无向图双向连接a[r].push_back(l);}// 从节点n开始DFS(此处根节点设为n)dfs(n, 1); // 参数:起始节点n,初始深度1chu(); // 预处理倍增表ll sum = 0;int x = 1; //1-i的lca;for (int i = 1; i <= n; i++) {if (i > 1) x = lca(x, i); // 计算当前x与i的LCAsum += head[x]; // 累加LCA的深度,及答案}cout << sum << endl;return 0;
}