CF697C-考虑二叉树的LCA的性质

传送门

题意

给你一个可以认为是无穷大的完全二叉树,初始边权均为0。有两种操作:

  • 输入u,v,w,把uv的唯一路径的每条边的边权都加上w
  • 输入u,v,求uv的路径长度

操作个数1000,点的编号范围1e18,1-indexed。

思路

树上唯一路径,必定是围绕LCA讨论。并且有一个常见且重要的性质:二叉树的点u的祖先(1-indexed),就是u的二进制的高若干位。所以求LCA的函数是可以十分方便地写出来的,模仿倍增LCA模板的一些技巧即可。

int DEP(LL x){
    int ans = 0;
    while(x) x >>= 1,++ans;
    return ans;
}

LL LCA(LL x,LL y){
    if(x < y) swap(x,y);
    int dx = DEP(x),dy = DEP(y);
    x >>= (dx-dy);
    if(x == y) return x;
    for(;x != y;x >>= 1,y >>= 1);
    return x;
}

又因为这个二叉树高度只有60,所以两种操作我们都可以直接暴力往上爬,爬到LCA为止。

暴力爬的过程需要获取边权,所以我们定义fa数组。fa[u]表示点uu>>1的边的边权。因为u值大,所以数组要用map代替。如果!fa.count(u),则认为边权为0。

于是两种操作都是很简单的。

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 100000 + 5;

int n,m;
map<LL,LL> fa;

void dbg(){puts("");}
template<typename T, typename... R>void dbg(const T &f, const R &... r) {
    cout << f << " ";
    dbg(r...);
}
template<typename Type>inline void read(Type &xx){
    Type f = 1;char ch;xx = 0;
    for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
    for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
    xx *= f;
}

int DEP(LL x){
    int ans = 0;
    while(x) x >>= 1,++ans;
    return ans;
}

LL LCA(LL x,LL y){
    if(x < y) swap(x,y);
    int dx = DEP(x),dy = DEP(y);
    x >>= (dx-dy);
    if(x == y) return x;
    for(;x != y;x >>= 1,y >>= 1);
    return x;
}

int main(int argc, char** argv) {
    read(m);
    while(m--){
        int op;read(op);
        if(op == 1){
            LL x,y,w;read(x);read(y);read(w);
            LL lca = LCA(x,y);
            for(;x != lca;x >>= 1) fa[x] += w;
            for(;y != lca;y >>= 1) fa[y] += w;
        }
        else{
            LL x,y;read(x);read(y);
            LL lca = LCA(x,y);
            LL ans = 0;
            for(;x != lca;x >>= 1){
                if(fa.count(x)) ans += fa[x];
            }
            for(;y != lca;y >>= 1){
                if(fa.count(y)) ans += fa[y];
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值