【 Codeforces Round #362 C】Lorenzo Von Matterhorn(思維)

 題意:

巴尼住在NYC。NYC具有從1開始的正整數編號的無數個交叉點。在交叉點 i 和 2i 之間以及 i和 2i + 1 之間存在雙向道路,對任意整數 i 都滿足。在任意兩點之間都有且只有一條最短路。

 

最初任何人都可以通過任何道路免費。 但是後來發生了 q 個事件。 有兩種類型的事件:

1. 政府制定新規。 一個規則可以用三個整數表示 vu 和 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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章