LCA定义为对于一颗树 树上两个点的最近公共祖先

一.Tarjan求LCA(离线方法

https://blog.csdn.net/lw277232240/article/details/77017517

二.倍增法求LCA

void dfs(int u, int f)
{
for(int i = ; i <= ; i++)
if(deep[u] >= (<<i))
fa[u][i] = fa[fa[u][i-]][i-];
for(int i = head[u];i;i = nxt[i])
{
int v = l[i].t;
if(v != f)
{
deep[v] = deep[u] + ;
dist[v] = dist[u] + l[i].d;
fa[v][] = u;
dfs(v, u);
}
}
}
int lca(int x, int y)
{
if(deep[x] < deep[y])
swap(x, y);
int delta = deep[x] - deep[y];
for(int i = ; i <= ; i++)
if((<<i) & delta)
x = fa[x][i];
for(int i = ; i >= ; i--)
if(fa[x][i] != fa[y][i])
{
x = fa[x][i];
y = fa[y][i];
}
if(x == y) return x;
else return fa[x][];
}
LL getdis(int x, int y)
{
int z = lca(x, y);
return dist[x] + dist[y] - * dist[z];
}

可以用来求一棵树上两点之间的最短距离

例题:

Gym 101808K 思路题

题意:给一个有n个点,n条边的图,n为1e5,查询任两点间的最短距离

思路:n个点n-1条边的话就是树,这个图就比树多了一条边,把这条边拿出来考虑

任意两点间的最短路有两种情况,一是经过这条边,二是不经过

建图的时候不加这一条边

x和y的距离 经过这条边的话x->uu + ww + vv->y或x->vv + ww + uu->y

不经过的话直接求即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int SZ = ;
const int INF = 1e9+;
int head[SZ], nxt[SZ], tot = , deep[SZ], fa[SZ][];
int fab[SZ];
LL dist[SZ];
struct node
{
int t, d;
}l[SZ];
void build(int f, int t, int d)
{
l[++tot].t = t;
l[tot].d = d;
nxt[tot] = head[f];
head[f] = tot;
}
int n;
void dfs(int u, int f)
{
for(int i = ; i <= ; i++)
if(deep[u] >= (<<i))
fa[u][i] = fa[fa[u][i-]][i-];
for(int i = head[u];i;i = nxt[i])
{
int v = l[i].t;
if(v != f)
{
deep[v] = deep[u] + ;
dist[v] = dist[u] + l[i].d;
fa[v][] = u;
dfs(v, u);
}
}
}
int lca(int x, int y)
{
if(deep[x] < deep[y])
swap(x, y);
int delta = deep[x] - deep[y];
for(int i = ; i <= ; i++)
if((<<i) & delta)
x = fa[x][i];
for(int i = ; i >= ; i--)
if(fa[x][i] != fa[y][i])
{
x = fa[x][i];
y = fa[y][i];
}
if(x == y) return x;
else return fa[x][];
}
LL getdis(int x, int y)
{
int z = lca(x, y);
return dist[x] + dist[y] - * dist[z];
}
void init()
{
memset(head, , sizeof(head));
tot = ;
memset(deep, , sizeof(deep));
memset(fa, , sizeof(fa));
for(int i = ; i <= n; i++) fab[i] = i;
}
int find(int x)
{
return x == fab[x] ? x : fab[x] = find(fab[x]);
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int q;
scanf("%d %d", &n, &q);
init();
int uu, vv, ww;
for(int i = ; i < n; i++)
{
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
int fu = find(u), fv = find(v);
if(fu == fv) uu = u, vv = v, ww = w;
else
{
fab[fu] = fv;
build(u, v, w);
build(v, u, w);
}
}
dist[] = ;
dfs(, );
//printf("%d %d\n", uu, vv);
//for(int i = 1; i <= n; i++) printf("%lld ", dist[i]);
while(q--)
{
int x, y;
scanf("%d %d", &x, &y);
LL dis1 = getdis(x, y);
LL dis2 = min(getdis(x, uu) + ww + getdis(y, vv), getdis(x, vv) + ww + getdis(y, uu));
printf("%lld\n", min(dis1, dis2));
}
}
return ;
}

Gym 101810M

题意:一棵树,每条边来回都可以获得不同的值,每条边只能去一次回一次,任意查询从s到t最多能获得多少值

思路:发现能获得的值是整棵树上的权值 - 从t走最短路到s获得的值

用dist[0][u]记录从根走到u总的值

用dist[1][u]记录从u走到根总的值

画个图推个式子就ok了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int SZ = ;
const int INF = 1e9+;
int head[SZ], nxt[SZ], tot = , deep[SZ], fa[SZ][];
int dist[][SZ];
struct node
{
int t, c1, c2;
}l[SZ];
void build(int f, int t, int c1, int c2)
{
l[++tot].t = t;
l[tot].c1 = c1;
l[tot].c2 = c2;
nxt[tot] = head[f];
head[f] = tot;
}
int n;
void dfs(int u, int f)
{
for(int i = ; i <= ; i++)
if(deep[u] >= (<<i))
fa[u][i] = fa[fa[u][i-]][i-];
for(int i = head[u];i;i = nxt[i])
{
int v = l[i].t;
if(v != f)
{
deep[v] = deep[u] + ;
dist[][v] = dist[][u] + l[i].c1;
dist[][v] = dist[][u] + l[i].c2;
fa[v][] = u;
dfs(v, u);
}
}
}
int lca(int x, int y)
{
if(deep[x] < deep[y])
swap(x, y);
int delta = deep[x] - deep[y];
for(int i = ; i <= ; i++)
if((<<i) & delta)
x = fa[x][i];
for(int i = ; i >= ; i--)
if(fa[x][i] != fa[y][i])
{
x = fa[x][i];
y = fa[y][i];
}
if(x == y) return x;
else return fa[x][];
}
void init()
{
memset(head, , sizeof(head));
tot = ;
memset(deep, , sizeof(deep));
memset(fa, , sizeof(fa));
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
init();
int sum = ;
for(int i = ; i < n-; i++)
{
int u, v, w1, w2;
scanf("%d %d %d %d", &u, &v, &w1, &w2);
build(u, v, w1, w2);
build(v, u, w2, w1);
sum = sum + w1 + w2;
}
dist[][] = , dist[][] = ;
dfs(, );
int q;
scanf("%d", &q);
while(q--)
{
int x, y;
scanf("%d %d", &x, &y);
int z = lca(x, y);
int ans = dist[][y] - dist[][z] + dist[][x] - dist[][z];
printf("%d\n", sum - ans);
}
}
return ;
}

RMQ:区间最值查询问题

用f[i][j]表示 从a[i] 开始 往后2^j个数里面的 最大/最小 值或GCD

void st(int n)
{
for(int i = ; i <= n; i++)
f[i][] = a[i];
for(int j = ; ( << j) <= n; j++)
for(int i = ; i + ( << j) - <= n; i++)
f[i][j] = max(f[i][j-], f[i + (<<(j-))][j-]);
}
int RMQ(int l, int r)
{
int k = ;
while((<<(k + ) <= r - l + )) k++;
return max(f[l][k], f[r - (<<k) + ][k]);
}

最小值把max改成min, GCD把max改成__gcd

例题:

POJ 2019 二维RMQ

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
const int SZ = ;
const int INF = 1e9+;
int a[SZ][SZ], mmax[SZ][SZ][], mmin[SZ][SZ][];
void st(int n)
{
for(int i = ; i <= n; i++)
for(int j = ; j <= n; j++)
mmax[i][j][] = mmin[i][j][] = a[i][j];
for(int j = ; ( << j) <= n; j++)
for(int i = ; i + ( << j) - <= n; i++)
for(int k = ; k <= n; k++)
{
mmax[k][i][j] = max(mmax[k][i][j-], mmax[k][i + (<<(j-))][j-]);
mmin[k][i][j] = min(mmin[k][i][j-], mmin[k][i + (<<(j-))][j-]);
}
}
int RMQ(int x, int l, int r, int b)
{
int k = ;
while((<<(k + ) <= r - l + )) k++;
int ans_max = -INF, ans_min = INF;
for(int i = x; i < x + b; i++)
{
ans_max = max(ans_max, max(mmax[i][l][k], mmax[i][r- (<<k) + ][k]));
ans_min = min(ans_min, min(mmin[i][l][k], mmin[i][r- (<<k) + ][k]));
}
return (ans_max - ans_min);
}
int main()
{
int n, b, k;
scanf("%d %d %d", &n, &b, &k);
for(int i = ; i <= n; i++)
for(int j = ; j <= n; j++)
scanf("%d", &a[i][j]);
st(n);
for(int i = ; i < k; i++)
{
int x, y;
scanf("%d %d", &x, &y);
int l = y, r = y + b - ;
printf("%d\n", RMQ(x, l, r, b));
}
return ;
}

HDU 5726

题意:给一段序列,任意查询一个区间内所有数的GCD,以及有多少个区间的GCD数和它相同

思路:RMQ+二分。。考场上有思路了但是没敢敲QAQ

发现序列越长,GCD是不增的,于是对于每个数可以通过二分判断它往后多少个数,这一段里面拥有相同的GCD

用map记录即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
typedef long long LL;
const int SZ = ;
const int INF = 1e9+;
int a[SZ];
int f[SZ][];
map<int, LL> mp;
void st(int n)
{
for(int i = ; i <= n; i++)
f[i][] = a[i];
for(int j = ; ( << j) <= n; j++)
for(int i = ; i + ( << j) - <= n; i++)
{
f[i][j] = __gcd(f[i][j-], f[i + (<<(j-))][j-]);
}
}
int RMQ(int l, int r)
{
int k = ;
while((<<(k + ) <= r - l + )) k++;
return __gcd(f[l][k], f[r - (<<k) + ][k]);
}
int main()
{
int T, tt = ;
scanf("%d", &T);
while(T--)
{
int n;
scanf("%d", &n);
mp.clear();
for(int i = ; i <= n; i++)
for(int j = ; j <= ; j++)
f[i][j] = ;
for(int i = ; i <= n; i++)
scanf("%d", &a[i]);
st(n);
for(int i = ; i <= n; i++)
{
int k = i;
while(k <= n)
{
int l = k, r = n;
while(l <= r)
{
int mid = (l + r + ) >> ;
if(RMQ(i, mid) < RMQ(i, k)) r = mid - ;
else l = mid + ;
}
mp[RMQ(i, k)] += LL(l - k);
k = l;
}
}
int q;
scanf("%d", &q);
printf("Case #%d:\n", ++tt);
while(q--)
{
int x, y;
scanf("%d %d", &x, &y);
int ans = RMQ(x, y);
printf("%d %lld\n", ans, mp[ans]);
}
}
return ;
}

暑假集训 || LCA && RMQ的更多相关文章

  1. 2015UESTC 暑假集训总结

    day1: 考微观经济学去了…… day2: 一开始就看了看一道题目最短的B题,拍了半小时交了上去wa了 感觉自己一定是自己想错了,于是去拍大家都过的A题,十分钟拍完交上去就A了 然后B题写了一发暴力 ...

  2. 【Homework】LCA&RMQ

    我校是神校,作业竟然选自POJ,难道不知道“珍爱生命 勿刷POJ”么? 所有注明模板题的我都十分傲娇地没有打,于是只打了6道题(其实模板题以前应该打过一部分但懒得找)(不过感觉我模板还是不够溜要找个时 ...

  3. POJ 2763 (LCA +RMQ+树状数组 || 树链部分) 查询两点距离+修改边权

    题意: 知道了一颗有  n 个节点的树和树上每条边的权值,对应两种操作: 0 x        输出 当前节点到 x节点的最短距离,并移动到 x 节点位置 1 x val   把第 x 条边的权值改为 ...

  4. STL 入门 (17 暑假集训第一周)

    快速全排列的函数 头文件<algorithm> next_permutation(a,a+n) ---------------------------------------------- ...

  5. 算法详解(LCA&RMQ&tarjan)补坑啦!完结撒花(。◕ˇ∀ˇ◕)

    首先,众所周知,求LCA共有3种算法(树剖就不说了,太高级,以后再学..). 1.树上倍增(ST表优化) 2.RMQ&时间戳(ST表优化) 3.tarjan(离线算法)不讲..(后面补坑啦!) ...

  6. 暑假集训Day2 互不侵犯(状压dp)

    这又是个状压dp (大型自闭现场) 题目大意: 在N*N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. ...

  7. 暑假集训Day1 整数划分

    题目大意: 如何把一个正整数N(N长度<20)划分为M(M>=1)个部分,使这M个部分的乘积最大.N.M从键盘输入,输出最大值及一种划分方式. 输入格式: 第一行一个正整数T(T<= ...

  8. UESTC 912 树上的距离 --LCA+RMQ+树状数组

    1.易知,树上两点的距离dis[u][v] = D[u]+D[v]-2*D[lca(u,v)] (D为节点到根节点的距离) 2.某条边<u,v>权值一旦改变,将会影响所有以v为根的子树上的 ...

  9. [LCA & RMQ] [NOIP2013] 货车运输

    首先看到这题, 由于要最大, 肯定是求最大生成树 那么 o(n2) dfs 求任意点对之间的最小边是可以想到的 但是看看数据范围肯定TLE 于是暴力出来咯, 不过要注意query的时候判断的时候要 m ...

随机推荐

  1. Jmeter学习之While Controller

    参考 https://www.cnblogs.com/richered/p/8404641.html https://blog.csdn.net/rwang99/article/details/511 ...

  2. 任务17:从UML角度来理解依赖

    什么是依赖 如果我们用EF操作数据库. 那么CustomerController就对Context形成了依赖. 我们这种依赖的写法就是隐式的依赖 显式依赖于隐式依赖 怎么理解隐式的依赖呢? 三层架构是 ...

  3. Spring之配置文件加载方式

    spring在org.springframework.core.io包中提供了多种配置文件加载方式.无论是XML.URL还是文件,都有很好的支持.比如基于URL的UrlResource.基于输入流的I ...

  4. [工具]kali-linux-2016.2 更新后

    使用官方的,会自动选择最近的服务器/etc/apt/sources.list # 就这一句就好了,不用添加一堆 deb http://http.kali.org/kali kali-rolling m ...

  5. CodeForces 622C

    题意: 给你一个数组,m个询问,l,r,x;让你输出在区间[ l , r ]上哪个位置不等于x. 思路: 额..我这个思路还是剽来的...不过真心赞啊. 开个p数组,直接记录数组每个元素的位置,并且实 ...

  6. python __builtins__ filter类 (24)

    24.'filter', 用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表.该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True ...

  7. CCF2016.4 - C题

    思路:先把路径按反斜杠split成数组,然后用一个ArrayList去模拟.如果遇到空或者.则不处理:如果遇到..则删除ArrayList最后一个元素(注意如果只有1个元素则不删除):其他情况直接加到 ...

  8. split("\\.")是什么意思

    \\会转义成反斜杠,反斜杠本身就是转义符,所有就成了“\.”,在进行转义就是.,所以\\.实际上是“.”.在java.lang包中也有String.split()方法,与.net的类似,都是返回是一个 ...

  9. 线段树(成段更新) POJ 3468 A Simple Problem with Integers

    题目传送门 /* 线段树-成段更新:裸题,成段增减,区间求和 注意:开long long:) */ #include <cstdio> #include <iostream> ...

  10. 单机版solr6.3和分布式solr6.3的安装部署

    一.单机版的solr部署 我的是在windows下安装的,linux同理 1. 安装JDK8,并配置好环境变量,一般我们经常开发的电脑上应该都有JDk了,所以这一步可以忽略. 2. 解压solr6.3 ...