CF697C-考虑二叉树的LCA的性质
Posted hans774882968
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF697C-考虑二叉树的LCA的性质相关的知识,希望对你有一定的参考价值。
题意
给你一个可以认为是无穷大的完全二叉树,初始边权均为0。有两种操作:
- 输入
u,v,w
,把u
到v
的唯一路径的每条边的边权都加上w
- 输入
u,v
,求u
到v
的路径长度
操作个数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]
表示点u
到u>>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;
}
以上是关于CF697C-考虑二叉树的LCA的性质的主要内容,如果未能解决你的问题,请参考以下文章