CF932D Tree 树上倍增

原题链接:https://codeforces.com/contest/932/problem/D

题意

一开始有一个初始节点1,权值为0,接下来会有一系列操作:

  1. R W 将第cnt个节点挂在第R个节点上,权值为W
  2. R W,询问从R开始的节点往上,如果遇到大于它的权值就加进来,总权值不能超过W,问能加进来多少个

分析

这题和 城市网络非常像,只不过这题是动态加点而已。

但这里多了一个限制,就是总权值之和。我们在记录倍增数组求祖先的时候,可以直接把权值也一起统计,记录 s u m [ i ] [ j ] sum[i][j] sum[i][j]代表从i节点开始,到第 2 j 2^j 2j个祖先位置的权值和。

那么转移方程和倍增找祖先是类似的

s u m [ i ] [ j ] = s u m [ i ] [ j − 1 ] + s u m [ f [ i ] [ j − 1 ] [ j − 1 ] sum[i][j]=sum[i][j-1]+sum[f[i][j-1][j-1] sum[i][j]=sum[i][j1]+sum[f[i][j1][j1]

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned int ul;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
const int N = 5e5 + 10;
const int M = 1e6 + 10;
const ll mod = 1e9 + 7;
const double eps = 1e-8;

#define lowbit(i) (i & -i)
#define Debug(x) cout << (x) << endl
#define fi first
#define se second
#define mem memset
#define endl '\n'

int f[N][25], a[N];
ll sum[N][25];

inline void solve() {
    int n, m; cin >> n;
    int cnt = 1;
    ll last = 0;
    for (int i = 1; i <= n; i++) {
        ll opt, x, w; cin >> opt >> x >> w;
        x ^= last;
        w ^= last;
        if (opt == 1) {
            ++cnt; a[cnt] = w;
            if (a[cnt] <= a[x]) {
                f[cnt][0] = x;
                sum[cnt][0] = a[x];
            }
            else {
                int now = x;
                for (int j = 20; ~j; j--) {
                    if (f[now][j] && a[f[now][j]] < w) {
                        now = f[now][j];
                    }
                }
                f[cnt][0] = f[now][0];
                sum[cnt][0] = a[f[now][0]];
            }
            for (int j = 1; j <= 20; j++) {
                f[cnt][j] = f[f[cnt][j-1]][j-1];
                sum[cnt][j] = sum[cnt][j-1] + sum[f[cnt][j-1]][j-1];
            }
        } else {
            if (a[x] > w) {
                cout << (last = 0) << endl;
                continue;
            }
            w -= a[x];
            int now = x;
            ll ans = 1;
            for (int j = 20; ~j; j--) {
                if (f[now][j] && w >= sum[now][j]) {
                    w -= sum[now][j];
                    now = f[now][j];
                    ans += (1 << j);
                }
            }
            cout << (last = ans) << endl;
        }

    }
}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值