唔 已经有一个多月没有写题解了, 其实这一个多月也是做了一些题目的,但是就是比较懒啊,所以都堆在一起没有写了~~。
题目链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=26842
题意: 给出一棵树(有默认根), 每个节点有一个权值,再给出q个询问,每个询问包含v k两个变量, 让你输出树根和v节点的路径上,权值大于等于k的离树根最近的节点。
思路: 算是倍增法 + dp的入门练习吧.
dp[v][i] 表示节点v ~ 它的2^i号祖先的路径上点权的最大值
fa[v][i] 表示节点v 的第2^i 号祖先
转移的时候就是 dp[v][i] = max(dp[v][i - 1], dp[fa[v][i-1]][i-1]) ,其转移的思路大致就是将v ~ 2^i的路径分成两段,
一段是v ~ 2^(i-1) 一段是2^(i-1) ~2^i
在处理询问的时候用的是类似二分的思想, 如果v的第2^i号祖先满足条件,我就把v节点变成2^i号祖先,否则把i--。
code:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define clr(x, y) memset(x, y, sizeof x)
#define inf 1000000000
using namespace std;
const int maxn = 100005;
const int maxp = 18;
typedef long long LL;
vector<int> G[maxn];
int fa[maxn][maxp];
int dp[maxn][maxp];
int val[maxn];
int n, q;
void dfs(int u)
{
int sz = G[u].size();
for (int i = 0; i < sz; i++)
{
int v = G[u][i];
fa[v][0] = u;
dp[v][0] = val[v]; //--* v ~ u的路径上不包括u
for (int j = 1; j < maxp; j++) fa[v][j] = fa[fa[v][j-1]][j-1];
for (int j = 1; j < maxp; j++) dp[v][j] = max(dp[v][j-1], dp[fa[v][j-1]][j-1]);
dfs(v);
}
}
int solve(int a, int b)
{
for (int i = maxp - 1; i >= 0; i--)
{
if (dp[fa[a][i]][i] >= b) a = fa[a][i];
}
return a;
}
int main()
{
int T;
int a, b, ff;
scanf("%d", &T);
for (int kk = 1; kk <= T; kk++)
{
scanf("%d%d", &n, &q);
for (int i = 0; i <= n; i++) G[i].clear();
val[0] = 1;
for (int i = 0; i < maxp; i++)
{
dp[0][i] = val[0];
fa[0][i] = 0;
}
for (int i = 1; i < n; i++)
{
scanf("%d%d", &ff, &val[i]);
G[ff].push_back(i);
}
dfs(0);
printf("Case %d:\n",kk);
while(q--)
{
scanf("%d%d", &a, &b);
printf("%d\n", solve(a, b));
}
}
return 0;
}