您的位置:首页 > 产品设计 > UI/UE

SPOJ Query on a tree II (倍增LCA)

2017-08-29 20:35 330 查看


QTREE2 - Query on a tree II

#graph-theory #tree

You are given a tree (an undirected acyclic connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. Each edge has an integer value
assigned to it, representing its length.
We will ask you to perfrom some instructions of the following form:
DIST a b : ask for the distance between node a and node b

or
KTH a b k : ask for the k-th node on the path from node a to node b
Example:
N = 6 

1 2 1 // edge connects node 1 and node 2 has cost 1 

2 4 1 

2 5 2 

1 3 1 

3 6 2 

Path from node 4 to node 6 is 4 -> 2 -> 1 -> 3 -> 6 
DIST 4 6 : answer is 5 (1 + 1 + 1 + 2 = 5) 
KTH 4 6 4 : answer is 3 (the 4-th node on the path from node 4 to node 6 is 3) 


Input

The first line of input contains an integer t, the number of test cases (t <= 25). t test cases follow.
For each test case:
In the first line there is an integer N (N <= 10000)
In the next N-1 lines, the i-th line describes the i-th edge: a line with three integers a b c denotes an edge between a, b of
cost c (c <= 100000)
The next lines contain instructions "DIST a b" or "KTH a b k"
The end of each test case is signified by the string "DONE".
There is one blank line between successive tests.


Output

For each "DIST" or "KTH" operation, write one integer representing its result.
Print one blank line after each test.


Example

Input:
1

6
1 2 1
2 4 1
2 5 2
1 3 1
3 6 2
DIST 4 6
KTH 4 6 4
DONE

Output:
5
3


题意:给了一颗n-1条边的树,有两种询问1:u,v的距离。2:u,v之间的第k个点。

思路:倍增lca板子而已, 可以树剖做

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e4 + 7;
const int maxm = 21;
int n, dis[maxn], dep[maxn], p[maxm][maxn], head[maxn], K;
struct node
{
int v, w, next;
node(){}
}edge[maxn*2];
void init()
{
memset(head, -1, sizeof(head));
K = 0;
}
void addEdge(int u, int v, int w)
{
edge[K].v = v;
edge[K].w = w;
edge[K].next = head[u];
head[u] = K++;
}
void dfs(int u, int f, int d)
{
dep[u] = d;
p[0][u] = f;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v;
if(to == f) continue;
dis[to] = dis[u] + edge[i].w;
dfs(to, u, d+1);
}
}
void build()  //构建lca
{
dfs(1, -1, 0);  //第一遍统计, 1是根节点
for(int i = 0; i+1 < maxm; i++)
{
for(int v = 1; v <= n; v++)
{
if(p[i][v] < 0) p[i+1][v] = -1;
else p[i+1][v] = p[i][p[i][v]];
}
}
}
int LCA(int u, int v)
{
if(dep[u] > dep[v]) swap(u, v);
for(int i = 0; i < maxm; i++)
{
if((dep[v]-dep[u])>>i&1)
v = p[i][v];
}
if(u == v) return u;
for(int i = maxm-1; i >= 0; i--)
{
if(p[i][u] != p[i][v])
{
u = p[i][u];
v = p[i][v];
}
}
return p[0][u];
}
int get_kth(int u, int v, int lca, int k) //寻找这条路径上第k个点
{
k--;  //因为u算第一个点, 所以k--
if(dep[u] - dep[lca] < k)  //如果第k个节点要跨过lca那个点,就等于求v到他那条链上距离为k'的点
{
k = dep[u] + dep[v] - dep[lca]*2 - k; //k'
u = v;
}
for(int i = 0; i < maxm; i++)  //倍增找距离这个节点为k的节点,注意这里是从0开始循环的,求距离内最远的某个点就要倒着了
if(k & (1<<i))
u = p[i][u], k ^= (1<<i);
return u;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
init();
int u, v, w;
for(int i = 1; i < n; i++)
{
scanf("%d%d%d", &u, &v, &w);
addEdge(u, v, w);
addEdge(v, u, w);
}
build();
char cmd[10];
while(1)
{
scanf("%s", cmd);
if(cmd[1] == 'O') break;
scanf("%d%d", &u, &v);
if(cmd[1] == 'I')
{
int lca = LCA(u, v);
printf("%d\n", dis[u] + dis[v] - 2*dis[lca]); //常规的求两点路径的距离
}
else
{
int k;
scanf("%d", &k);
int lca = LCA(u, v);
printf("%d\n", get_kth(u, v, lca, k));
}
}
}
return 0;
}




这是另一道差分+二分的题,以前用二分写得,现在倍增写了写(尴尬的是,倍增比二分慢。。)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
const int maxm = 20;
ll ans[maxn], dis[maxn], p[maxm][maxn], a[maxn], cnt[maxn];
struct node
{
int v;
ll w;
node(){}
node(int vv, ll ww) : v(vv), w(ww){}
};
vector<node> v[maxn];
void dfs(int x, int f)
{
p[0][x] = f;
for(int i = 1; i < maxm; i++)   //处理每个节点,1-maxm所有的情况
p[i][x] = p[i-1][p[i-1][x]];
//    for(int i = 0; i+1 < maxm; i++)
//         if(p[i][x] < 0) p[i+1][x] = -1;
//            else p[i+1][x] = p[i][p[i][x]];
int u = x;
for(int i = maxm-1; i >= 0; i--)   //算某个最近的距离, 要从大往小枚举, 注意要加一个u>1
while(u > 1 && dis[x] - dis[p[i][u]] <= a[x])
u = p[i][u];
u = max(1, u);  //最后一定要看看飞没飞到根节点外
cnt[p[0][x]]++;  //差分
cnt[p[0][u]]--;
for(int i = 0; i < v[x].size(); i++)
{
int to = v[x][i].v;
if(to == f) continue;
dis[to] = dis[x] + v[x][i].w;
dfs(to, x);
}
}
void dfs_ans(int x, int f)  //算差分, 从底层往上算
{
for(int i = 0; i < v[x].size(); i++)
{
int to = v[x][i].v;
if(to == f) continue;
dfs_ans(to, x);  //不断递归
cnt[x] += cnt[to];  //回溯的时候,就算出了差分
}
}
int main()
{
int n;
while(~scanf("%d", &n))
{
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
int x, y;
for(int i = 1; i < n; i++)
{
scanf("%d%d", &x, &y);
v[x].push_back(node(i+1, y));
}
dfs(1, -1);
dfs_ans(1, -1);
for(int i = 1; i <= n; i++)
printf("%lld%c", cnt[i], i == n ? '\n' : ' ');
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: