hdu 3078(LCA的在线算法)

Network

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 847    Accepted Submission(s): 347

Problem Description

The
ALPC company is now working on his own network system, which is
connecting all N ALPC department. To economize on spending, the backbone
network has only one router for each department, and N-1 optical fiber
in total to connect all routers.
The usual way to measure connecting
speed is lag, or network latency, referring the time taken for a sent
packet of data to be received at the other end.
Now the network is on
trial, and new photonic crystal fibers designed by ALPC42 is trying
out, the lag on fibers can be ignored. That means, lag happened when
message transport through the router. ALPC42 is trying to change routers
to make the network faster, now he want to know that, which router, in
any exactly time, between any pair of nodes, the K-th high latency is.
He needs your help.

Input

There are only one test case in input file.
Your
program is able to get the information of N routers and N-1 fiber
connections from input, and Q questions for two condition: 1. For some
reason, the latency of one router changed. 2. Querying the K-th longest
lag router between two routers.
For each data case, two integers N and Q for first line. 0<=N<=80000, 0<=Q<=30000.
Then n integers in second line refer to the latency of each router in the very beginning.
Then N-1 lines followed, contains two integers x and y for each, telling there is a fiber connect router x and router y.
Then
q lines followed to describe questions, three numbers k, a, b for each
line. If k=0, Telling the latency of router a, Ta changed to b; if
k>0, asking the latency of the k-th longest lag router between a and b
(include router a and b). 0<=b<100000000.
A blank line follows after each case.

Output

For
each question k>0, print a line to answer the latency time. Once
there are less than k routers in the way, print "invalid request!"
instead.

Sample Input

5 5
5 1 2 3 4
3 1
2 1
4 3
5 3
2 4 5
0 1 2
2 2 3
2 1 4
3 3 5

Sample Output

3
2
2
invalid request!

题意:给出n个点,m条询问,依次输入n个点都有权值,以及n-1条边,接下来m条询问每次输入 k a b如果 k=0 ,则更新a点的权值为b,如果k>0,则输出a-b这条路径上权值第K大的点,如果没有第K大点,输出invalid request!

今天做这个题,正好借这个题总结一下LCA的在线算法。

上图为测试用例:

LCA的在线算法,先要对此树进行预处理(为以后的RMQ做准备),即进行深搜。

我们设立三个数组:ver,first,deep ver数组保存树的前序遍历结点的结果,first保存结点第一次出现的位置,deep保存每个节点出现的深度

对此树进行先序遍历:

ver: 1 2 1 3 4 3 5 3 1

deep:1 2 1 2 3 2 3 2 1

first:1 2 4 5 7

从这里可以知道 ver和deep都要开两倍大小。

然后如果查询结点 2 5,先找到2以及5第一次出现的位置,first[2] = 2,first[5] = 7

那么2和5的最近公共祖先必定在ver[2]-ver[7]区间中。那么接下来,我们只要拿出这段区间内深度最小的点,那么这个点就肯定是2 5的最近公共祖先了

所以这里找区间最小值我们就可以利用RMQ算法了(这里RMQ的数组保存的是下标,因为deep和ver数组是通过下标联系起来的)。找到了2 1 2 3 2 3中

深度最小的是deep[3],对应ver中的ver[3]=1,所以2 和 5的最近公共祖先是1

RMQ算法:http://dongxicheng.org/structure/lca-rmq/

了解了过程,代码也就能够写出来了。

然后关于这题:我们要找a - b中的第K大点,那么我们记录一个父亲数组,u->lca,v->lca反着找就OK。

/*
5 5
5 1 2 3 4 3 1 2 1 4 3 5 3
2 4 5
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include <string.h>
#include <math.h>
#define N 80005
using namespace std;

struct Edge{
    int u,v,next;
}edge[2*N];
int head[N];
int deep[2*N];
int vis[N];
int first[N];
int ver[2*N];
int father[N];
int dp[2*N][25];
int value[N];
int path[N];
int tot;

void add_edge(int u,int v,int &k){  ///Á´Ê½Ç°ÏòÐÇ
    edge[k].u = u,edge[k].v = v;
    edge[k].next = head[u];head[u] = k++;
}
void dfs(int u,int dep,int pre){
    vis[u]=true,ver[++tot]=u,first[u]=tot,deep[tot]=dep,father[u]=pre;
    for(int k=head[u];k!=-1;k=edge[k].next){
        if(!vis[edge[k].v]){
            dfs(edge[k].v,dep+1,u);
            ver[++tot] = u,deep[tot]=dep;
        }
    }
}
int MIN(int i,int j){
    if(deep[i]<deep[j]) return i;
    return j;
}
void init_RMQ(int n){
    for(int i=1;i<=n;i++) dp[i][0]=i;
    for(int j=1;(1<<j)<=n;j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
            dp[i][j] = MIN(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        }
    }
}
int RMQ(int L,int R){
    int k = (int)(log(R-L+1.0)/log(2.0));
    int a = dp[L][k];
    int b = dp[R-(1<<k)+1][k];
    return MIN(a,b);
}
int LCA(int a,int b){
    int x = first[a],y = first[b];
    //printf("%d %d\n",deep[x],deep[y]);
    if(x>y) swap(x,y);
    //printf("RMQ(x,y): %d\n",RMQ(x,y));
    return ver[RMQ(x,y)];
}
int cmp(int a,int b){
    return a>b;
}
void solve(int k,int u,int v){
    int lca = LCA(u,v);
    tot=0;
    while(u!=lca){
        path[tot++] = value[u];
        u = father[u];
    }
    while(v!=lca){
        path[tot++] = value[v];
        v = father[v];
    }
    //printf("lca = %d\n",lca);
    path[tot++] = value[lca];
    //printf("path[tot-1] = %d\n",path[tot-1]);
    if(k>tot){
        printf("invalid request!\n");
        return;
    }
    sort(path,path+tot,cmp);
    //for(int i=0;i<tot;i++) printf("%d ",path[i]);
    printf("%d\n",path[k-1]);
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(head,-1,sizeof(head));
        memset(father,-1,sizeof(father));
        for(int i=1;i<=n;i++){
            scanf("%d",&value[i]);
        }
        tot = 0;
        for(int i=1;i<n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            add_edge(u,v,tot);
            add_edge(v,u,tot);
        }
        tot = 0;
        dfs(1,1,-1);
        init_RMQ(2*n-1);
        while(m--){
            int k,a,b;
            scanf("%d%d%d",&k,&a,&b);
            if(k==0){
                value[a]=b;
            }else{
                solve(k,a,b);
            }
        }
    }
    return 0;
}
时间: 2024-12-05 02:37:12

hdu 3078(LCA的在线算法)的相关文章

LCA(倍增在线算法) codevs 2370 小机房的树

codevs 2370 小机房的树 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花费太多精力.已知从某个节点爬到其父亲节点要花费 c 的能量(从父亲节点爬到此节点也相同),他们想找出一条花费精力最短的路,以使得搞基的时候精力旺盛,他们找到你要你设计

浅谈LCA的在线算法

最近在学双连通分量,做到一个题,是LCA的,不会做就来学习了一下LCA,发现网上有好多资料,鱼龙混杂.推荐一篇因为他推荐了许多文章(感兴趣点击打开)) 首先先借鉴一下他的一张图片 这基本就是这个lca的思想. 按照我的理解首先要dfs一边求出first [u],deep[u],并且记录下first[u]所对应的u: 代码如下: void dfs(int u ,int dep) { vis[u] = true; ver[++tot] = u; first[u] = tot; deep[tot] =

HDU 3078 (LCA+树链第K大)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3078 题目大意:定点修改.查询树中任意一条树链上,第K大值. 解题思路: 先用离线Tarjan把每个Query树链的LCA求出来. LCA中对连接树Dfs的时候,令p[v]=u,记录v的前驱. LCA结束后,对于每个Query: 从u开始回溯到LCA,记录值.从v开始回溯到LCA,记录值. 再加上LCA这个点的值,形成一条完整树链.特判树链长度是否小于K. 对树链中的值,从大到小排序,取第K大即可

hihocoder1069最近公共祖先&#183;三(LCA在线算法--DFS+RMQ-ST)

树上任意两点的最近祖先,必定就是这两个节点的最短路径上深度最小的那个点. 例如:下图中,节点7和5,其最短路径为7--4--1--5, 这条路径上深度最小的点为节点1,其深度为1.节点1即为节点7和5的LCA. 因此,要找到任意两个节点的LCA,只需要先找到上述最短路径,再找到最短路径中深度最小的点.而这下面所述LCA在线算法所做的事. LCA在线算法描述(以上图为例): 1.获得“最短路径”(并不是真正的一条路径,包含其他节点,但不影响算法的正确性) 采用DFS遍历整棵树,得到以下数据: (1

HDU 2874 LCA离线算法

Connections between cities Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4477    Accepted Submission(s): 1284 Problem Description After World War X, a lot of cities have been seriously damage

LCA最近公共祖先 在线算法和离线算法 模板

原理讲解:http://dongxicheng.org/structure/lca-rmq/ 在线算法模板: /** LCA在线算法O(nlogn) 主函数调用: init(); tot=0,dir[1]=0; dfs(1,1); ST(2*n-1); int lca=LCA(u,v); */ #include <stdio.h> #include <string.h> #include <algorithm> #include <iostream> #in

LCA在线算法ST算法

求LCA(最近公共祖先)的算法有好多,按在线和离线分为在线算法和离线算法. 离线算法有基于搜索的Tarjan算法较优,而在线算法则是基于dp的ST算法较优. 首先说一下ST算法. 这个算法是基于RMQ(区间最大最小值编号)的,不懂的可以这里学习一些 而求LCA就是把树通过深搜得到一个序列,然后转化为求区间的最小编号. 比如说给出这样一棵树. 我们通过深搜可以得到这样一个序列: 节点ver 1 3 1 2 5 7 5 6 5 2 4 2 1 (先右后左) 深度R 1 2 1 2 3 4 3 4 3

hdu 2586 LCA模板题(离线算法)

http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B&quo

HDU 4547 LCA倍增算法

CD操作 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 1111    Accepted Submission(s): 297 Problem Description 在Windows下我们可以通过cmd运行DOS的部分功能,其中CD是一条很有意思的命令,通过CD操作,我们可以改变当前目录. 这里我们简化一下问题,假设只有一个根目录,