P4216 [SCOI2015]情报传递 树剖+主席树

原题链接:https://www.luogu.com.cn/problem/P4216

题意

简单描述一下题意,有一棵树,m次操作

  1. 将x节点打上标记,并且x节点的权值每回合会增加1
  2. 询问x到y路径上有多少节点以及有多少节点权值大于c

分析

树上路径问题,一般直接想到树剖,然后考虑怎么去取值。先离线把打标记的时间记录一下,记为节点的权值,然后我们每次查询值的时候就只要查 [ 1 , t − v a l − 1 ] [1, t-val-1] [1,tval1]权值的点个数就可以了,可以用主席树轻松维护。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;
const int M = 1000010;
const int MOD = 1e9 + 7;
vector<int> g[N];
struct node {
    int ls, rs, sum;
}hjt[N*50];
struct Query {
    int k, x, y, c, t;
}q[N];
int rt[N], cnt, Rt, a[N], m;
int f[N][25];
void modify(int &now, int pre, int l, int r, int pos) {
    now = ++cnt;
    hjt[now] = hjt[pre];
    hjt[now].sum ++;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (pos <= mid) modify(hjt[now].ls, hjt[pre].ls, l, mid, pos);
    else modify(hjt[now].rs, hjt[pre].rs, mid+1, r, pos);
}
int query(int u, int v, int lca, int fa_lca, int l, int r, int ql, int qr) {
    if (ql <= l && qr >= r) return hjt[u].sum + hjt[v].sum - hjt[lca].sum - hjt[fa_lca].sum;
    int mid = (l + r) >> 1;
    int ans = 0;
    if (ql <= mid) ans += query(hjt[u].ls, hjt[v].ls, hjt[lca].ls, hjt[fa_lca].ls, l, mid, ql, qr);
    if (qr > mid) ans += query(hjt[u].rs, hjt[v].rs, hjt[lca].rs, hjt[fa_lca].rs, mid+1, r, ql, qr);
    return ans;
}
int son[N], siz[N], dep[N], fat[N], dfn[N], rnk[N], top[N], tot;
void dfs1(int u, int fa) {
    son[u] = -1; siz[u] = 1; dep[u] = dep[fa] + 1; fat[u] = fa;
    f[u][0] = fa;
    for (int i = 1; i <= 20; i++) f[u][i] = f[f[u][i-1]][i-1];
    modify(rt[u], rt[fa], 0, m, a[u]);
    for (auto v : g[u]) {
        if (v == fa) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if (son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
    }
}

void dfs2(int u, int t) {
    rnk[dfn[u] = ++tot] = u; top[u] = t;
    if (son[u] == -1) return;
    dfs2(son[u], t);
    for (auto v : g[u]) {
        if (v != son[u] && v != fat[u]) dfs2(v, v);
    }
}

int lca(int x, int y) {
    if (dep[x] > dep[y]) swap(x, y);
    for (int i = 20; i >= 0; i--) if (dep[y] - dep[x] >= (1 << i)) y = f[y][i];

    if (x == y) return x;
    for (int i = 20; i >= 0; i--) {
        if (f[y][i] != f[x][i])
            y = f[y][i], x = f[x][i];
    }
    return f[x][0];
}
void solve () {
    int n; cin >> n;
    for (int i = 1; i <= n; i++) {
        int x; cin >> x;
        if (!x) Rt = i;
        else g[i].push_back(x), g[x].push_back(i);
    }
    cin >> m;
    for (int i = 1; i <= n; i++) a[i] = m + 1;
    for (int i = 1; i <= m; i++) {
        cin >> q[i].k;
        if (q[i].k == 1) {
            cin >> q[i].x >> q[i].y >> q[i].c;
        } else {
            cin >> q[i].t;
            a[q[i].t] = i;
        }
    }
    dfs1(Rt, 0);
    dfs2(Rt, Rt);
    for (int i = 1; i <= m; i++) {
        if (q[i].k == 1) {
            int _lca = lca(q[i].x, q[i].y);
            int fa_lca = f[_lca][0];
            int num1 = dep[q[i].x] + dep[q[i].y] - 2*dep[_lca] + 1;
            int num2 = query(rt[q[i].x], rt[q[i].y], rt[_lca], rt[fa_lca], 0, m, 0, max(0, i - q[i].c - 1));
            cout << num1 << " " << num2 << 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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值