poj 1330 树上LCA+RMQ

本文介绍了一种基于树状结构实现的区间最小值查询(RMQ)算法。该算法利用深度优先搜索(DFS)遍历树形结构,并通过预处理的方式计算区间最小值,以高效解决给定路径上的最小值查询问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
const int N = 10005;
const int M = 101;
const int MAX = 1000000000;
int n;
struct Edge
{
   int u,v,next;
}edge[N];
int head[N],tot = 0;
void addedge(int u, int v)
{
   tot++;
   edge[tot].u = u;
   edge[tot].v = v;
   edge[tot].next = head[u];
   head[u] = tot;
}

int first[N];//结点在搜索顺序数组中最先出现的位置(下标)
int occur[2*N];//结点在出现的顺序数组重复的也要记录
int depth[2*N];//结点在搜索树中的深度,与occur相对应
int dp_min[2*N][20];//dp_min[i][j] 表示从第i个位置开始的2^j个元素中的最小值的下标
int m = 0;//不断记录出现的下标
void dfs(int u, int deep)
{
   occur[++m] = u;//进入该点时进行记录
   depth[m] = deep;
   if(!first[u])
   {
      first[u] = m;
   }
   for(int i = head[u]; i != 0; i = edge[i].next)
   {
      dfs(edge[i].v, deep+1);
      occur[++m] = u;//访问子树返回也要标记
      depth[m] = deep;
   }
}

void init()
{
   tot = 0;
   m = 0;
   memset(head,0,sizeof(head));
   memset(first,0,sizeof(first));
   int in[N] = {false};//记录结点有无入度
   memset(in,false,sizeof(in));
   int u=0,v=0;
   cin>>n;
   for(int i = 1; i < n; i++)
   {
      cin>>u>>v;
      addedge(u,v);//这里一定是u->v  否则要加双向边
      in[v] = true;
   }
   for(int i = 1; i <= n; i++)//保证要从根开始dfs
   {
      if(!in[i])
      {
         dfs(i,1);
         break;
      }
   }
}
void rmq_init()
{
   for(int i = 1; i <= m; i++)
   {
      dp_min[i][0] = i;
   }
   for(int j = 1,s = 1; s <= m; s = (1<<(j++)))
   {
      for(int i = 1; i+s < m; i++)
      {//由于要求的是最小值的下标这里采用间接比较,将长为s的区间分为相等的两端求最小值,以2的幂不断增加s,最终的解
         dp_min[i][j] = depth[dp_min[i][j-1]] < depth[dp_min[i+s][j-1]] ? dp_min[i][j-1] : dp_min[i+s][j-1];
      }
   }
}
int rmq_min(int a, int b)
{
   int l=first[a],r=first[b];//得到区间的左右端点
   if(l > r)
   {
      int t = l;
      l = r;
      r = t;
   }
   int k = floor(log(double(r-l+1))/log(2.0));
   int s = 1<<k;
   int min_id = depth[dp_min[l][k]] < depth[dp_min[r-s+1][k]] ? dp_min[l][k] : dp_min[r-s+1][k];
   return occur[min_id];
}
int main()
{
   int t = 0;
   int a = 0, b = 0;
   cin>>t;
   while(t--)
   {
      init();
      rmq_init();
      cin>>a>>b;
      cout<<rmq_min(a,b)<<endl;
   }
   return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值