Codeforces 1380E Merging Towers(LCA+倍增)

题目

思路

树建模+LCA倍增+前缀和
PS:一道很好的训练题,能够学到很多技巧和知识

代码

#include <bits/stdc++.h>

#define ll long long
using namespace std;
const int N = 200043;
const int L = 20;
int tin[2 * N];
int tout[2 * N];
int p[2 * N];//记录节点i的父节点p[i];
int idx[N];//记录idx[i],数字i在哪个idx[i]个Tower
int dept = 0;//用来记录欧拉序列的深度
int dp[2 * N][L];//用来进行LCA的倍增算法,dp[i][j]表示节点i往上的第2^j个祖先节点
vector<int> g[N * 2];//用于记录树节点的child list
void dfs(int root, int father) {
    //题意说明最后m-1个查询会将m个Tower合并成最后一个Tower
    tin[root]=dept++;
    dp[root][0] = father;
    p[root] = father;
    for (int i = 1; i < L; i++) {
        dp[root][i] = dp[dp[root][i - 1]][i - 1];
    }
    for (auto child:g[root]) {
        dfs(child, root);
    }
    tout[root]=dept++;
}

bool isAncestor(int x, int y) {
    return tin[x] <= tin[y] && tout[x] >= tout[y];
}

int lca(int x, int y) {
    if (isAncestor(x, y))
        return x;
    for (int i = L - 1; i >= 0; i--) {
        if (!isAncestor(dp[x][i], y)) {
            x = dp[x][i];
        }
    }
    return p[x];
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> idx[i];
        idx[i]--;
    }
    vector<int> cur(m, 0);
    for (int i = 0; i < m; i++) cur[i] = i;
    for (int i = 0; i < m - 1; i++) {
        int x, y;
        cin >> x >> y;
        x--;
        y--;
        int nidx = i + m;
        g[nidx].push_back(cur[x]);
        g[nidx].push_back(cur[y]);
        cur[x] = nidx;//合并后的新id
    }
    //树上LCA 记录dp[i][j] 节点i的第2^j个父节点
    int root = 2 * m - 2;
    dfs(root, root);
    vector<int> psum(m, 0);
    for (int i = 0; i < n - 1; i++) {
        int t = lca(idx[i], idx[i + 1]);//求两个连续节点所在的Tower的最近公共祖先
        if (t < m) {
            psum[0]++;//pair<i,i+1>在同一个tower里面,不需要移动
        } else psum[t - m + 1]++;
    }
    for (int i = 0; i < m - 1; i++) {
        psum[i + 1] += psum[i];
    }
    for (int i = 0; i < m; i++) {
        cout << n - 1 - psum[i] << endl;
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值