題意:
巴尼住在NYC。NYC具有從1開始的正整數編號的無數個交叉點。在交叉點 i 和 2i 之間以及 i和 2i + 1 之間存在雙向道路,對任意整數 i 都滿足。在任意兩點之間都有且只有一條最短路。
最初任何人都可以通過任何道路免費。 但是後來發生了 q 個事件。 有兩種類型的事件:
1. 政府制定新規。 一個規則可以用三個整數表示 v, u 和 w。經過這次操作,點 u 到點 v 路徑上的所有點增加 w 元路費。
2. 巴尼從點 v 通過最短路移動到點 u。
現巴尼想知道,他的每一次移動需要花多少錢
Input
第一行包含一個整數 q (1 ≤ q ≤ 1 000).
之後 q 行,每行包含一個事件。加路費的事件都被描述成 1 v u w ,代表將點 u 到點 v 路徑上所有點路費增加 w 元。移動的事件都被描述成 2 v u,表示巴尼從點 v 走到點 u.
1 ≤ v, u ≤ 1018, v ≠ u, 1 ≤ w ≤ 109。
Output
對於第二類的每個事件,打印Barney在此事件中通過的所有道路的通行費的總和,一行。 以相應事件的時間順序打印答案。
Example
Input
7 1 3 4 30 1 4 1 2 1 3 6 8 2 4 3 1 6 1 40 2 3 7 2 2 4
Output
94 0 32
思路:
由給的樣例的圖可以看出來,這是一個樹,所以對於u到v的路徑可以分爲兩部分u和v的公共祖先到u、v。找公共祖先就是將兩者不斷除2,看看哪一次相等。對一條鏈加權值時,可以轉換爲對每兩個點之間的邊加權值。一次加邊最多加120多的邊,1000次也就120000個元素,最後查詢時,查詢每條邊的權值再加和。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<vector>
#include<unordered_map>
#define mod (1000000007)
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
const int inf=0x3f3f3f3f;
ll getfa(ll x,ll y){//求公共祖先、需要開longlong
if(x==y) return x;
if(x>y) swap(x,y);
return getfa(x,y/2);
}
struct node{
ll l,r,w;
}wi[maxn];
int cnt=0;
map<pair<ll,ll>,ll>mp;
void add(ll x,ll y,ll w){對每個邊加權值
ll ty=y/2,las=y;
while(ty>=x){
mp[make_pair(ty,las)]+=w;
las=ty;ty/=2;
}
}
ll getsum(ll x,ll y){//查詢
ll sum=0,ty=y/2,las=y;
while(ty>=x) sum+=mp[make_pair(ty,las)],las=ty,ty/=2;;
return sum;
}
int main(){
int q,op;
ll u,v,w;
scanf("%d",&q);
while(q--){
scanf("%d",&op);
if(op==1){
scanf("%lld%lld%lld",&u,&v,&w);
ll fa=getfa(u,v);
if(fa!=u) add(fa,u,w);
if(fa!=v) add(fa,v,w);
}
else{
scanf("%lld%lld",&u,&v);
ll fa=getfa(u,v);
ll sum=0;
sum+=getsum(fa,u);
sum+=getsum(fa,v);
printf("%lld\n",sum);
}
}
return 0;
}