LightOJ - 1128 (倍增法 + dp)

唔 已经有一个多月没有写题解了, 其实这一个多月也是做了一些题目的,但是就是比较懒啊,所以都堆在一起没有写了~~。


题目链接: 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值