Codeforces Round 981 (Div. 3) G
G. Sakurako and Chefir
因为没有找到类似的题解,顺便记录下来
题目
给定一棵树,树上有
为了帮助樱子,浩介记录了他的
此外,在每次猜测中,浩介都假设切菲尔可以沿着边移动任意多次:
- 从顶点
到顶点 ,如果 是 的祖先,体力不会发生变化; - 从顶点
到顶点 ,如果 不是 的祖先,那么切菲尔的体力会减少 。
如果切菲尔的体力为
对于每个假设,你的任务是找出切菲尔在拥有
分析
在一颗树中,某个点最远的一个点一定可以作为直径的一个端点(参考2次bfs/dfs求直径)
对于每次查询,
所以求出每棵子树中的直径端点,每次的最远点就是这两个点之一
在树dp求直径的时候顺便求出端点
细节在代码中有注释
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
const int N=1e6+10;
const int mod=998244353;
const int INF = 0x3f3f3f3f;
const ll INFll = 0x3f3f3f3f3f3f3f3f;
#define endl "\n"
vector<vector<int>>adj(N);
struct gflca{ // 简单的lca
int n, m, root;
vector<vector<int>> fa;
vector<int> dep;
void init(int _n, int _m, int _root) {
n = _n + 10;
m = _m;
root = _root;
fa.resize(n);
dep.resize(n);
for(int i = 0; i < n; i ++) {
fa[i].resize(m);
dep[i] = 0;
for(int j = 0; j < m; j ++) fa[i][j] = 0;
}
dfs(root, 0);
}
void dfs(int u, int father) {
dep[u] = dep[father] + 1;
fa[u][0] = father;
for(int i = 1; (1 << i) <= dep[u]; i ++) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
}
for(auto v: adj[u]) {
if(v != father) dfs(v, u);
}
}
int LCA(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for(int i = m - 1; i >= 0; i --) {
if(dep[x] - (1 << i) >= dep[y]) x = fa[x][i];
}
if(x == y) return x;
for(int i = m - 1; i >= 0; i --) {
if(fa[x][i] != fa[y][i]) {
x = fa[x][i];
y = fa[y][i];
}
}
return fa[x][0];
}
int len(int x, int y) {
return dep[x] + dep[y] - 2*dep[LCA(x, y)];
}
int find(int u, int k) { // 查找u的第k个祖先,注意特判0
for(int i = m - 1; i >= 0; i --) {
if(((k >> i) & 1)) {
u = fa[u][i];
}
}
if(u == 0) u = root;
return u;
}
}lca;
PII d1[N],d2[N], d[N]; //d1[u]存的是以u一个端点,另一个点是u子树中的叶子所组成的最长的一条链,这里存了长度和这个叶子编号
// d2和d1差不多,只不过是第二长的
// d 就u为根的子树直径的两个端点
int len[N]; // len 为直径长度
void dfs(int u, int fa) {
d1[u] = d2[u] = {0, u};
for(auto v : adj[u]) {
if(fa == v) continue;
dfs(v, u);
// 很经典的树dp求直径的方法,只不过多求了个端点 注意最长链和次长链不能来源同一个儿子
if(d1[v].first + 1 >= d1[u].first) {
d2[u] = d1[u];
d1[u] = d1[v];
d1[u].first += 1;
} else if(d1[v].first + 1 >= d2[u].first) {
d2[u] = d1[v];
d2[u].first += 1;
}
}
len[u] = d1[u].first + d2[u].first;
d[u] = {d1[u].second, d2[u].second}; // 直径过u
for(auto v : adj[u]) { // 直径在某个子树内
if(v == fa) continue;
if(len[v] > len[u]) {
len[u] = len[v];
d[u] = d[v];
}
}
}
void solve()
{
int n; cin >> n;
for(int i = 1; i < n; i ++) {
int u, v; cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
lca.init(n,25,1);
dfs(1, -1);
int q; cin >> q;
while(q --) {
int u, k; cin >> u >> k;
int fa = lca.find(u, k);
cout << max(lca.len(u, d[fa].first), lca.len(u,d[fa].second)) << " ";
}
cout << endl;
for(int i = 1; i <= n; i ++) {
len[i] = 0;
d[i] = d1[i] = d2[i] = {0, 0};
adj[i].clear();
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cout << setprecision(11) << fixed;
int t;t=1;
cin>>t;
for(int i=1;i<=t;i++){
//printf("Case %d: ",i);
solve();
}
}
分类:
codeforce
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】