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全国联赛提高组

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 钻石 Diamond
 
题目描述 Description

A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入描述 Input Description

第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。

输出描述 Output Description

输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。

样例输入 Sample Input

4 3 
1 2 4 
2 3 3 
3 1 1 
3
1 3 
1 4 
1 3

样例输出 Sample Output

3
-1
3

数据范围及提示 Data Size & Hint

对于 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专题的更多相关文章

  1. 倍增法求LCA

    倍增法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先. 倍增法是通过一个数组来实现直接找到一个节点的某个祖先,这样我们就可 ...

  2. HDU 2586 倍增法求lca

    How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  3. 倍增法求lca(最近公共祖先)

    倍增法求lca(最近公共祖先) 基本上每篇博客都会有参考文章,一是弥补不足,二是这本身也是我学习过程中找到的觉得好的资料 思路: 大致上算法的思路是这样发展来的. 想到求两个结点的最小公共祖先,我们可 ...

  4. 树上倍增法求LCA

    我们找的是任意两个结点的最近公共祖先, 那么我们可以考虑这么两种种情况: 1.两结点的深度相同. 2.两结点深度不同. 第一步都要转化为情况1,这种可处理的情况. 先不考虑其他, 我们思考这么一个问题 ...

  5. 倍增法求LCA(最近公共最先)

    对于有根树T的两个结点u.v,最近公共祖先x=LCA(u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大. 如图,根据定义可以看出14和15的最近公共祖先是10,   15和16的最近公共 ...

  6. LCA 在线倍增法 求最近公共祖先

    第一步:建树  这个就不说了 第二部:分为两步  分别是深度预处理和祖先DP预处理 DP预处理: int i,j; ;(<<j)<n;j++) ;i<n;++i) ) fa[i ...

  7. 倍增法求lca:暗的连锁

    https://loj.ac/problem/10131 #include<bits/stdc++.h> using namespace std; struct node{ int to, ...

  8. 倍增法求LCA代码加详细注释

    #include <iostream> #include <vector> #include <algorithm> #define MAXN 100 //2^MA ...

  9. 浅谈倍增法求解LCA

    Luogu P3379 最近公共祖先 原题展现 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入格式 第一行包含三个正整数 \(N,M,S\),分别表示树的结点个数.询问 ...

随机推荐

  1. Visual Studio 2013 Unit Test Project App.config文件设置方法

    开放中经常会要做单元测试,新的项目又没有单元测试项目,怎么才能搭建一个单元测试项目呢? 下面跟我四步走,如有错误之处,还请指正! 1.添加项目 2.添加配置文件 新建app.config文件,注意不是 ...

  2. Spring MVC 处理静态资源文件

    摘要: 三个方案: 1.方案一:激活Tomcat的defaultServlet来处理静态文件 2.方案二: 在spring3.0.4以后版本提供了mvc:resources (需要配置annotati ...

  3. thinkphp怎么设置输入网址直接进入首页

    1.设置apache服务器的时候,文件的位置要写包含index.php的那个主文件夹. 2.apache和thinkphp默认index.html是作为网站的默认首页,所以在浏览器端可以直接输入网址进 ...

  4. js 与JQuery显示及隐藏方法

    虽然以后两种方式都能让文本信息隐藏和显示 第一种文本隐藏以后还是会占居位置, 第二种则不会占位置. <p id="p1">这是一段文本.</p> <i ...

  5. 9、ASP.NET MVC入门到精通——Controller(控制器)

    本系列目录:ASP.NET MVC4入门到精通系列目录汇总 Controller主要负责响应用户的输入.主要关注的是应用程序流,输入数据的处理,以及对相关视图(View)输出数据的提供. 继承自:Sy ...

  6. 《对象及DOM知识点及其应用1》

    1.图片间的来回切换用if{}else{}; <超链接方式的切换图片(常用)>如:<a href="../images1/1-small.jpg" id=&quo ...

  7. [Cordova] Plugin里使用iOS Framework

    [Cordova] Plugin里使用iOS Framework 前言 开发Cordova Plugin的时候,在Native Code里使用第三方Library,除了可以加速项目的时程.也避免了重复 ...

  8. 分享20个最新的免费 UI 设计素材给设计师

    用户界面设计涉及到很多的创意,灵感以及需要与客户进行有效沟通的技能.良好的用户界面是一致的,可以使网站更容易理解和使用.UI设计的重点在于用户体验和互动,同时易于使用对于一个成功的移动应用程序来说非常 ...

  9. Struts2(1) —— 概述

    1.Struts2框架介绍 Struts2框架是MVC流程框架,适合分层开发,框架应用实现不依赖于Servlet,使用大量的拦截器来处理用户请求,属于无侵入式的设计. 2.Struts2框架的流程原理 ...

  10. View and Data API tips: 缓存Access Token

    对于云API服务,常见的方式就是按照API调用次数收费,某些API调用也就有某些限制,比如在特定时间内只允许调用指定的次数以免造成滥用.虽然Autodesk的view and Data API目前还没 ...