本文主要是介绍Codeforces697C Lorenzo Von Matterhorn,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Lorenzo Von Matterhorn
Barney lives in NYC. NYC has infinite number of intersections numbered with positive integers starting from 1. There exists a bidirectional road between intersections i and 2i and another road between i and 2i + 1 for every positive integer i. You can clearly see that there exists a unique shortest path between any two intersections.
Initially anyone can pass any road for free. But since SlapsGiving is ahead of us, there will q consecutive events happen soon. There are two types of events:
1. Government makes a new rule. A rule can be denoted by integers v, u and w. As the result of this action, the passing fee of all roads on the shortest path from u to v increases by w dollars.
2. Barney starts moving from some intersection v and goes to intersection u where there's a girl he wants to cuddle (using his fake name Lorenzo Von Matterhorn). He always uses the shortest path (visiting minimum number of intersections or roads) between two intersections.
Government needs your calculations. For each time Barney goes to cuddle a girl, you need to tell the government how much money he should pay (sum of passing fee of all roads he passes).
Input
The first line of input contains a single integer q (1 ≤ q ≤ 1 000).
The next q lines contain the information about the events in chronological order. Each event is described in form 1 v u w if it's an event when government makes a new rule about increasing the passing fee of all roads on the shortest path from u to v by w dollars, or in form 2 v u if it's an event when Barnie goes to cuddle from the intersection v to the intersection u.
1 ≤ v, u ≤ 1018, v ≠ u, 1 ≤ w ≤ 109 states for every description line.
Output
For each event of second type print the sum of passing fee of all roads Barney passes in this event, in one line. Print the answers in chronological order of corresponding events.
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
题意:有一棵无限多的满二叉树,根结点的值为1,两个子结点的数分别为两倍的根结点和两倍的根结点加一。
一开始每条边的权值均为0。
现在有两种操作,操作1是将两个结点之间所有路径的边权值加w。
操作2是给你两个结点为要你求出两结点的路径长度。
解法:暴力
由于树的特性,其实每次的更新或是查找相关的结点并不多。所以可以用一个map<pair, int>存储每两个结点间的权值。
对于两个数要想找到他们的路径,我们首先需要找到两者的最近公共祖先,其实就是二进制位高位相等的位数。
比如对于数6和5 二进制分别为110和101那么1就是两者的lca,首先需要把5->2->1的权值更新,再更新1->3->6。
于是从高到低比较两者的二进制,然后先向lca更新,再向下更新。
由于数据量不大,用map暴力就可以。就是编码可能有一些些复杂。
#include <bits/stdc++.h>
#include <cstdio>
#define ll long long
#define __max(a,b) a > b ? a : b
#define pll pair<ll, ll>
using namespace std;
const int maxn = 1e5 + 10;
map<pll, ll> power;
int U[100], V[100];
ll f, u, v, w;
int main()
{
// freopen("/Users/vector/Desktop/out.txt", "w", stdout);ios::sync_with_stdio(false);cin.tie(0);int q;cin >> q;while(q--){cin >> f >> u >> v;if(f == 1) cin >> w;if(u > v)swap(u, v);int iu = 0, iv = 0;ll uu = u, vv = v;while(uu > 0){U[iu++] = uu % 2;uu /= 2;}while(vv > 0){V[iv++] = vv % 2;vv /= 2;}int i;for(i = 0; i < iu; i++){if(U[iu - i - 1] != V[iv - i - 1])break;}if(f == 1){if(i < iu)//向上更新{for(int j = i; j <= iu - 1; j++){power[make_pair(u, u / 2)] += w;power[make_pair(u / 2, u)] += w;//注意每次要记得双向更新u /= 2;}}for(int j = i; j < iv; j++)//向下更新{if(V[iv - j - 1]){power[make_pair(u, (u << 1) + 1)] += w;power[make_pair(u * 2 + 1, u)] += w;u = (u << 1)+ 1;}else{power[make_pair(u, u << 1)] += w;power[make_pair(u << 1, u)] += w;u = u << 1;}}}else//查询两点间路径时也类似{ll ans = 0;if(i < iu){for(int j = i; j <= iu - 1; j++){ans += power[make_pair(u, u / 2)];u /= 2;}}for(int j = i; j < iv; j++){if(V[iv - j - 1]){ans += power[make_pair(u, (u << 1) + 1)];u = (u << 1)+ 1;}else{ans += power[make_pair(u, u << 1)];u = u << 1;}}cout << ans << endl;}}return 0;
}
事实上,这样写还是写麻烦了。
更好的办法是,由于从子结点向父结点就只有一条路径。所以可以直接用map存每个结点到父结点的权值。
每次两个值循环向上找公共祖先。( 大的数不断的除以2)
这样的代码就很elegant了。
#include <bits/stdc++.h>
#include <cstdio>
#define ll long long
#define __max(a,b) a > b ? a : b
#define pll pair<ll, ll>
using namespace std;
const int maxn = 1e5 + 10;
map<ll, ll> power;
bool U[100], V[100];
ll f, u, v, w;
int main()
{// freopen("/Users/vector/Desktop/out.txt", "w", stdout);ios::sync_with_stdio(false);cin.tie(0);int q;cin >> q;while(q--){cin >> f >> u >> v;if(f == 1) cin >> w;if(u > v)swap(u, v);if(f == 1){while(u != v){if(u > v)swap(u, v);power[v] += w;v /= 2;}}else{ll ans = 0;while(u != v){if(u > v)swap(u, v);ans += power[v];v /= 2;}cout << ans << endl;}}return 0;
}
这篇关于Codeforces697C Lorenzo Von Matterhorn的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!