在线倍增法求LCA专题
1.cojs 186. [USACO Oct08] 牧场旅行
★★ 输入文件:pwalk.in
输出文件:pwalk.out
简单对比
时间限制:1 s 内存限制:128 MB
n个被自然地编号为1..n奶牛(1<=n<=1000)正在同样被方便的编号为1..n的n个牧场中吃草。更加自然而方便的是,第i个奶牛就在第i个牧场中吃草。
其中的一些对牧场被总共的n-1条双向通道的一条连接。奶牛可以通过通道。第i条通道连接的两个牧场是A_i和B_i(1<=A_i<=N;1<=B_i<=N)其长度是L_i(1<=L_i<=10000)。
通道只会连接两个不同的牧场,所以这些通道使得整个牧场构成了一棵树。
奶牛们是好交际的希望能够经常的访问别的奶牛。急切地,它们希望你能通过告诉它们Q(1<=Q<=1000)对牧场的路径来帮助他们安排旅行。(这里将有Q个询问,p1,p2(1<=p1<=n;1<=p1<=n))
分数:200
问题名称:pwalk
输入格式:
- 第1行:两个用空格隔开的整数:n和Q
- 第2..n行:第i+1行包含三个用空格隔开的整数:A_i,B_i和L_i
- 第n+1..N+Q行:每行包含两个用空格隔开的整数,代表两个不同的牧场,p1和p2
输入样例(file pwalk.in):
4 2
2 1 2
4 3 2
1 4 3
1 2
3 2
输出格式:
- 第1..Q行:行i包含第i个询问的答案。
输出样例:
2
7
输出说明:
询问1:牧场1和牧场2的路径长度为2。 询问2:3->4->1->2;总长为7。
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#define Er 20
#define N 1011
int deepp[N],head[N];
int father[N][Er];
struct Edge{
int v,last,w;
}edge[N<<];
int t=,n,Q,ai,bi,li;
int p1,p2;
typedef long long ll;
ll dis[N]={};
void add_edge(int u,int v,int w)
{
++t;
edge[t].v=v;
edge[t].w=w;
edge[t].last=head[u];
head[u]=t;
}
void input()
{
scanf("%d%d",&n,&Q);
for(int i=;i<=n-;++i)
{
scanf("%d%d%d",&ai,&bi,&li);
add_edge(ai,bi,li);
add_edge(bi,ai,li);
}
}
void dfs(int k)
{
for(int l=head[k];l;l=edge[l].last)
{
if(!deepp[edge[l].v])
{
deepp[edge[l].v]=deepp[k]+;
dis[edge[l].v]=dis[k]+edge[l].w;
father[edge[l].v][]=k;
dfs(edge[l].v);
}
}
}
void pre_chuli()
{
for(int j=;(<<j)<=n;++j)
for(int i=;i<=n;++i)
if(father[i][j-]!=-)
father[i][j]=father[father[i][j-]][j-];
}
int lca(int a,int b)
{
if(deepp[a]<deepp[b]) swap(a,b);
int i;
for(i=;(<<i)<=deepp[a];++i);
i--;
for(int j=i;j>=;--j)
if(deepp[a]-deepp[b]>=(<<j))
a=father[a][j];
if(a==b) return a;
for(int j=i;j>=;--j)
if(father[a][j]!=-&&father[a][j]!=father[b][j])
{
a=father[a][j];
b=father[b][j];
}
return father[a][];
}
int main()
{
freopen("pwalk.in","r",stdin);
freopen("pwalk.out","w",stdout);
input();
memset(father,-,sizeof(father));
dis[]=;
deepp[]=;
dfs();
pre_chuli();
for(int i=;i<=Q;++i)
{
scanf("%d%d",&p1,&p2);
int ance=lca(p1,p2);
cout<<dis[p1]+dis[p2]-*dis[ance]<<endl;
}
fclose(stdin);fclose(stdout);
return ;
}
2.cojs 2075. [ZLXOI2015][异次元圣战III]ZLX的陨落
★★☆ 输入文件:ThefallingofZLX.in
输出文件:ThefallingofZLX.out
简单对比
时间限制:1 s 内存限制:256 MB
【题目描述】
正当革命如火如荼,情侣教会日薄西山之时,SOX和FFF的分歧却越来越大,SOX认为情侣教会不合适的、无限制的秀恩爱和阶级歧视值得反对,而FFF认为一切情侣都该从这个世界上消失。
SOX先发制人,率先接管了全国政权并突袭了FFF,暗杀了FFF的首领,FFF的红色革命事业遭到了空前的打击,一些FFF团员积极抵抗,另一些FFF团员隐居避世,而一些FFF团员则走向极端,参加了一个邪恶组织:诅咒教会
你虽然对SOX下山摘桃子的行为不满,但你对邪教更不满。在对诅咒教会的长期调查中,你发现该组织的操纵者是亡灵巫师ZLX!现在,维护正义的使命降到了你身上!你和其他的一些远征军将士前往ZLX的城堡,却掉入了ZLX的魔法陷阱--扭曲虚空,扭曲虚空由n个魔法结界空间组成,m条虚空隧道连接着它们,你和其他的远征军将士(恰好有n个)分散在魔法结界空间里,只有会合在一起,你们才能冲破封锁(扭曲虚空是一颗树)。现在,你向平行世界的你提出了疑问,如果给出两个人会合,总共至少需要多少魔法能量?
已知虚空隧道的长度与消耗的魔法能量在数值上相等。
ZLX的末日已经到临,等到你冲出虚空隧道,亲手结果了ZLX吧!
【输入格式】
第一行一个正整数N,代表魔法结界空间的个数,一个正整数M,代表虚空隧道的个数
接下来M行,每行三个数u,v,w,代表虚空隧道连接的两个点和虚空隧道的长度
接下来一个正整数Q,代表查询个数
接下来Q行,每行两个数u,v代表询问从u到v需要消耗的魔法能量
【输出格式】
Q行,每行一个正整数
【样例输入】
6 5
1 2 7
1 3 3
2 4 5
3 5 7
4 6 6
5
3 4
6 3
5 1
4 3
4 2
【样例输出】
15
21
10
15
5
【提示】
对于20%的数据,n<=300,q<=300
对于40%的数据,n<=2000,q<=2000
对于100%的数据,n<=100000,q<=100000,w<=32767,m=n-1
#define N 100100
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#define Er 20
typedef long long ll;
ll dis[N]={};
int deepp[N],father[N][Er],t,n,m,u1,v1,w1,Q;
struct Edge{
int v,last,w;
}edge[N<<];
int head[N];
void add_edge(int u,int v,int w)
{
++t;
edge[t].v=v;
edge[t].w=w;
edge[t].last=head[u];
head[u]=t;
}
void input()
{
scanf("%d%d",&n,&m);
for(int i=;i<=m;++i)
{
scanf("%d%d%d",&u1,&v1,&w1);
add_edge(u1,v1,w1);
add_edge(v1,u1,w1);
}
}
void dfs(int k)
{
for(int l=head[k];l;l=edge[l].last)
{
if(!deepp[edge[l].v])
{
deepp[edge[l].v]=deepp[k]+;
father[edge[l].v][]=k;
dis[edge[l].v]=dis[k]+edge[l].w;
dfs(edge[l].v);
}
}
}
void pre_chuli()
{
for(int j=;(<<j)<=n;++j)
for(int i=;i<=n;++i)
if(father[i][j-]!=-)
father[i][j]=father[father[i][j-]][j-];
}
int lca(int a,int b)
{
if(deepp[a]<deepp[b]) swap(a,b);
int i;
for(i=;(<<i)<=deepp[a];++i);
i--;
for(int j=i;j>=;--j)
if(deepp[a]-deepp[b]>=(<<j))
a=father[a][j];
if(a==b) return a;
for(int j=i;j>=;--j)
if(father[a][j]!=-&&father[a][j]!=father[b][j])
{
a=father[a][j];
b=father[b][j];
}
return father[a][];
}
int main()
{
freopen("ThefallingofZLX.in","r",stdin);
freopen("ThefallingofZLX.out","w",stdout);
input();
memset(father,-,sizeof(father));
dis[]=;
deepp[]=;
dfs();
pre_chuli();
scanf("%d",&Q);
for(int i=;i<=Q;++i)
{
scanf("%d%d",&u1,&v1);
int ance=lca(u1,v1);
cout<<dis[u1]+dis[v1]-*dis[ance]<<endl;
}
fclose(stdin);fclose(stdout);
return ;
}
3.codevs 3287 货车运输
2013年NOIP全国联赛提高组
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
3
-1
3
对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q < 1,000;
对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q < 1,000;
对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q < 30,000,0 ≤ z ≤ 100,000。
/*--------------解析---------------------*/
拿到这道题,我们求的是多组两点间通路上最大的最小边权,最大生成树的两点间最小边权就满足这个性质,所以就用到到最大生成树 很容易得到60分算法 60分算法:(最大生成树暴力) 先找一个最大生成树,然后把生成树单独放在一个类似生成树存储方式的三个数组里,初始化并查集,然后从最大边开始做kruskal,每次连边时,都对问题集合进行扫描,当且仅当这两个边联通且没有更新过当前问题的答案,就用当前边权更新。原因很简单,这是贪心,首先边权时降序的,然后这个边使两点第一次联通,那么这个边一定是通路上最小的边。这就是60的算法。但是注意,题目并没有说是不是一棵树,所以它可以是森林,但是由于六十分算法原理对于森林这个性质并不依赖,所以,这个60算法不用考虑森林 那么我们想一下,这道题时间的瓶颈在哪,首先kruskal是o(m)的,这个不会爆时间,那么剩下就是从最大生成树上回答答案所用时间过长。 我们想一下,如果是在logn时间内回答一个问题,而且是在树上做的一个算法,尤其是这个问题具有区间求和的性质,因为想一想就知道这是显然的 f[i→j]:=min(f[i→k],f[k→j]), 所以,我们想到用树上倍增算法解决。 100分算法(最大生成树+树上倍增): 首先清楚树上倍增原理:f[i,j]:=f[f[I,j-],j-] f[I,j],表示 i向上2^j的节点 同时,设我们要求的答案g[i,j],即从i向上走到达2^j层的节点所通过路径上的最小值,转移方程 g[I,j]:=min(g[f[I,j-],j-],g[I,j-]) 分析一下就是显然的,g[f[I,j-],j-]表示的是i向上2^(j-)的节点到2^j节点间的最小值,g[I,j-] 是从i向上走到达2^(j-)层的节点所通过路径上的最小值,两个区间合起来就是当前的区间长度,所以这个方程成立。对于初值,我们可以bfs求深度并记录,与此同时,f[I,]是父节点,g[I,]是他向上走一层的边的长度。然后按照倍增算法模板生成f g数组 求解时,用倍增算法模板即可,只不过有几个细节 、 别忘了给ans赋初值 、 倍增算法永远是倍增到最近公共祖先的下一层的两个点,所以最后要比较一下ans与那两个点的向上走一步的值 、 记得这是个森林,要用一个数组记一下那个是父节点,我用的是并查集加bool数组,有个细节是不要直接把fa[i]当I的所在树的根,要get一下,因为并查集的压缩路径是在每次get时才更新,当他fa变了,而他未被查询过,就会导致fa过时了,要重新get一下 、 在对齐两个所求节点的层数时,别忘了更新答案 大概就是这些,还有相关代码具体实现就不赘述了。 哦,对了,提交不要忘了删除调试用的输出
/*
60分代码:暴力建q棵最大生成树
*/
#define N 10008
#define M 50007
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<algorithm>
int n,m,q,fa[N],head[N],t=;
struct Edge{
int u,v,w,last;
bool operator <(Edge P)
const{return w>P.w;}
}edge[M<<];
int read()
{
int ret=,ff=;
char s=getchar();
while(s<''||s>'')
{
if(s=='-') ff=-;
s=getchar();
}
while(s>=''&&s<='')
{
ret=ret*+s-'';
s=getchar();
}
return ret;
}
inline void add_edge(int u,int v,int w)
{
++t;
edge[t].u=u;
edge[t].v=v;
edge[t].w=w;
edge[t].last=head[u];
head[u]=t;
}
inline void inpu()
{
n=read();m=read();
int x,y,z;
for(int i=;i<=m;++i)
{
x=read();y=read();z=read();
add_edge(x,y,z);
}
}
int find(int x)
{
return (fa[x]==x)?x:fa[x]=find(fa[x]);
}
int main()
{
freopen("truck.in","r",stdin);
freopen("truck.out","w",stdout);
inpu();
sort(edge+,edge+t+);
q=read();
int x,y;
for(int i=;i<=q;++i)
{
x=read();y=read();
for(int i=;i<=n;++i) fa[i]=i;
bool flag=false;
for(int i=;i<=t;++i)
{
int x1=find(edge[i].u);
int y1=find(edge[i].v);
if(x1!=y1)
{
fa[y1]=x1;
}
if(find(x)==find(y))
{
printf("%d\n",edge[i].w);
flag=true;
break;
}
}
if(!flag) printf("-1\n");
}
fclose(stdin);
fclose(stdout);
return ;
}
#define N 10010
#define inf 10000800
#include<cstdio>
#define M 50002
#define D 18
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,m,q,fa[N],t=;
struct EEdge{
int u,v,w;
bool operator <(EEdge P)
const{return w>P.w;}
}edge1[M];
struct Edge{
int v,w,last;
}e[N];
int dis[N][D+],fath[N][D+],dep[N],head[N];
int read()
{
int ret=,ff=;
char s=getchar();
while(s<''||s>'')
{
if(s=='-') ff=-;
s=getchar();
}
while(s>=''&&s<='')
{
ret=ret*+s-'';
s=getchar();
}
return ret*ff;
}
void inpu()
{
n=read();m=read();
for(int i=;i<=m;++i)
{
edge1[i].u=read();
edge1[i].v=read();
edge1[i].w=read();
}
}
int fi(int x)
{
return (fa[x]==x)?x:fa[x]=fi(fa[x]);
}
void add_edge(int u,int v,int w)
{
++t;
e[t].v=v;
e[t].w=w;
e[t].last=head[u];
head[u]=t;
}
void ddkruskal()
{
for(int i=;i<=n;++i) fa[i]=i;
sort(edge1+,edge1+m+);
int tsum=;
for(int i=;i<=m;++i)
{
int x1=fi(edge1[i].u);
int x2=fi(edge1[i].v);
if(x1!=x2)
{
tsum++;
fa[x2]=x1;
add_edge(edge1[i].u,edge1[i].v,edge1[i].w);
add_edge(edge1[i].v,edge1[i].u,edge1[i].w);
if(tsum==n-) break;
}
}
}
void dfs(int k)
{
for(int l=head[k];l;l=e[l].last)
{
if(!dep[e[l].v])
{
dep[e[l].v]=dep[k]+;
fath[e[l].v][]=k;
dis[e[l].v][]=e[l].w;
dfs(e[l].v);
}
}
}
void init()
{
for(int j=;j<=D;++j)
for(int i=;i<=n;++i)
{
fath[i][j]=fath[fath[i][j-]][j-];
dis[i][j]=min(dis[i][j-],dis[fath[i][j-]][j-]);
}
}
int lca(int a,int b)
{
int ans=inf;
if(dep[a]<dep[b]) swap(a,b);
for(int j=D;j>=;--j)
{
if(dep[a]-(<<j)>=dep[b])
{
ans=min(ans,dis[a][j]);
a=fath[a][j];
}
}
if(a==b) return ans;
for(int j=D;j>=;--j)
{
if(fath[a][j]!=fath[b][j])
{
ans=min(ans,min(dis[a][j],dis[b][j]));
a=fath[a][j];
b=fath[b][j];
}
}
ans=min(ans,min(dis[a][],dis[b][]));
return ans;
}
int main()
{
freopen("truck.in","r",stdin);
freopen("truck.out","w",stdout);
inpu();
ddkruskal();
q=read();
int x,y;
for(int i=;i<=n;++i)
{
if(!dep[i])
{
dep[i]=;
fath[i][]=i;
dis[i][]=inf;
dfs(i);
}
}
init();
for(int i=;i<=q;++i)
{
x=read();y=read();
if(fi(x)!=fi(y)) printf("-1\n");
else printf("%d\n",lca(x,y));
}
fclose(stdin);
fclose(stdout);
return ;
}
在线倍增法求LCA专题的更多相关文章
- 倍增法求LCA
倍增法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先. 倍增法是通过一个数组来实现直接找到一个节点的某个祖先,这样我们就可 ...
- HDU 2586 倍增法求lca
How far away ? Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- 倍增法求lca(最近公共祖先)
倍增法求lca(最近公共祖先) 基本上每篇博客都会有参考文章,一是弥补不足,二是这本身也是我学习过程中找到的觉得好的资料 思路: 大致上算法的思路是这样发展来的. 想到求两个结点的最小公共祖先,我们可 ...
- 树上倍增法求LCA
我们找的是任意两个结点的最近公共祖先, 那么我们可以考虑这么两种种情况: 1.两结点的深度相同. 2.两结点深度不同. 第一步都要转化为情况1,这种可处理的情况. 先不考虑其他, 我们思考这么一个问题 ...
- 倍增法求LCA(最近公共最先)
对于有根树T的两个结点u.v,最近公共祖先x=LCA(u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大. 如图,根据定义可以看出14和15的最近公共祖先是10, 15和16的最近公共 ...
- LCA 在线倍增法 求最近公共祖先
第一步:建树 这个就不说了 第二部:分为两步 分别是深度预处理和祖先DP预处理 DP预处理: int i,j; ;(<<j)<n;j++) ;i<n;++i) ) fa[i ...
- 倍增法求lca:暗的连锁
https://loj.ac/problem/10131 #include<bits/stdc++.h> using namespace std; struct node{ int to, ...
- 倍增法求LCA代码加详细注释
#include <iostream> #include <vector> #include <algorithm> #define MAXN 100 //2^MA ...
- 浅谈倍增法求解LCA
Luogu P3379 最近公共祖先 原题展现 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入格式 第一行包含三个正整数 \(N,M,S\),分别表示树的结点个数.询问 ...
随机推荐
- Redis所需内存 超过可用内存怎么办
爬虫和转载请注明原文地址:博客园蜗牛 http://www.cnblogs.com/tdws/p/5727633.html Redis所需内存 超过可用内存怎么办 Redis修改数据多线程并发—Red ...
- 一个简单的WPF字体选择器实现
很久没有写博客了. 这是放暑假中的第一篇博客,以后会多多更新!!! 这就是我写的一个字体选择器,界面如下: 本程序用到的技术比较简单,仅仅是用了Font类的几个方法和数据绑定而已. 首先建一个四行两列 ...
- 06-图1 列出连通集 (25分)(C语言邻接表实现)
题目地址:https://pta.patest.cn/pta/test/558/exam/4/question/9495 由于边数E<(n*(n-1))/2 所以我选用了邻接表实现,优先队列用循 ...
- 20个编写现代 CSS 代码的建议
明白何谓Margin Collapse 不同于其他很多属性,盒模型中垂直方向上的Margin会在相遇时发生崩塌,也就是说当某个元素的底部Margin与另一个元素的顶部Margin相邻时,只有二者中的较 ...
- angular源码分析:angular中入境检察官$sce
一.ng-bing-html指令问题 需求:我需要将一个变量$scope.x = '<a href="http://www.cnblogs.com/web2-developer/&qu ...
- js限制文本框只能输入数字方法小结
有时需要限制文本框输入内容的类型,本节分享下正则表达式限制文本框只能输入数字.小数点.英文字母.汉字等代码. 例如,输入大于0的正整数 代码如下: <input onkeyup="i ...
- SharePoint 2013 入门教程之创建页面布局及页面
在SharePoint的使用过程中,页面布局和页面时很重要的两个概念,主要用于数据个性化展示,下面,我们简单介绍一下SharePoint的页面布局和页面的个性化. 一. SharePoint页面模型概 ...
- SharePoint 2013 Designer系列之数据视图筛选
在SharePoint中,我们经常需要对列表进行简单的筛选,这时,数据视图就有作用了,我们可以定制对于字段的筛选,来进行展示:特别的,筛选不同于搜索,并没有对于附件或者文档的全文检索,如果需要全文检索 ...
- ks
http://www.codeproject.com/Articles/207820/The-Repository-Pattern-with-EF-code-first-Dependen
- eclipse启动时报错 Could not create the java virtual machine
eclipse启动的时候 报错 这是系统为eclipse分配的内存不足,需要去修改 eclipse.ini文件 eclipse.ini是eclipse 内存分配之类的配置文件 对java虚拟机的 ...