给你Q个操作,对应操作:
1 u v w 表示从u到v的树上最短路径上的每一条无向边的权值都加上w
2 u v 表示查询从u到v的树上最短路径上的每一条无向边的权值加和
思路:
这是一个不需要Tarjan---LCA算法的一道最近公共祖先的问题。
1、看到10^18的点的数量,就表想建边建点构图的问题了。
2、这是一颗二叉树.所以两点间的树上最短路径所经过的每一条边都是固定的,其实就是在说,找这两个点的最近公共祖先,然后分别从这两个点到其公共祖先的距离和就是两点间的树上最短路径。
3、那么根据上述的特点,我们完全可以保存每一条边上的权值,并且维护加和,那么我们用什么来保存其值呢?map映射可以解决这个问题,我们用任何数组都做不到的事情,map映射就能够解决。s【点u】=x表示从u出发到其祖先(u/2)的权值和,那么我们在输入的时候,维护这个map映射值即可。
4、在操作1的时候,因为是二叉树,所以其寻找最近公共祖先就用两个点的编号大的编号除以2,一直除到两个点编号变成一样的,那么这个一样的编号就是其公共祖先,维护权值到这里,停止跳出。在操作2的时候,跟操作1一样,只不过操作1是维护权值和,操作2用来累加这个和。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
using namespace std;
#define ll long long int
struct node
{
ll u,v,w;
}a[1000000];
int main()
{
int q;
map<ll ,ll >h;
scanf("%d",&q);
int cont=0;
while(q--)
{
ll ans=0;
int op;
scanf("%d",&op);
if(op==1)
{
ll u,v,w;
scanf("%I64d%I64d%I64d",&u,&v,&w);
while(u!=v)
{
if(u>v)
{
h[u]+=w;
u/=2;
}
else
{
h[v]+=w;
v/=2;
}
}
}
if(op==2)
{
ans=0;
ll u,v;
scanf("%I64d%I64d",&u,&v);
ll ans=0;
while(u!=v)
{
if(u>v)
{
ans+=h[u];
u/=2;
}
else
{
ans+=h[v];
v/=2;
}
}
printf("%I64d\n",ans);
}
}
}