Vijos P1460 拉力赛 倍增+LCA/时间戳

因为最近在学LCA,所以一看到这道题就果断码了 倍增+LCA。这道题本质就是判断u是否为v的祖先,AC代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #define maxn 233333
 4 struct node{
 5     int to,next,w;
 6 };
 7 node e[maxn];
 8 int n,m,cnt,pre[maxn],p[maxn][20],len[maxn],count,dis[maxn];
 9 long long sum;
10 void build(int,int,int);
11 void dfs(int);
12 void ycl();
13 void lca(int,int);
14 void find(int,int);
15 int main(){
16     scanf("%d %d",&n,&m);
17     cnt=0;
18     for(int i=1;i<n;i++){
19         int u,v,c;
20         scanf("%d %d %d",&u,&v,&c);
21         build(u,v,c);
22     }
23     len[1]=1;dis[1]=0;
24     dfs(1);
25     ycl();
26     count=0;sum=0;
27     for(int i=1;i<=m;i++){
28         int x,y;
29         scanf("%d %d",&x,&y);
30         lca(x,y);
31     }
32     printf("%d\n%lld",count,sum);
33     return 0;
34 }
35 void build(int u,int v,int c){
36     cnt++;
37     e[cnt].to=v;e[cnt].w=c;e[cnt].next=pre[u];pre[u]=cnt;
38 }
39 void dfs(int x){
40     for(int i=pre[x];i;i=e[i].next){
41         int to=e[i].to;
42         dis[to]=e[i].w+dis[x];
43         len[to]=len[x]+1;
44         p[to][0]=x;
45         dfs(to);
46     }
47 }
48 void ycl(){
49     for(int j=1;(1<<j)<=n;j++)
50        for(int i=1;i<=n;i++)
51           p[i][j]=p[p[i][j-1]][j-1];
52 }
53 void lca(int a,int b){
54     int x,y;
55     if(len[a]>len[b]) return;
56     x=a;y=b;
57     int fc=len[b]-len[a];
58     for(int j=0;(1<<j)<=fc;j++)
59        if((1<<j)&fc) b=p[b][j];
60     if(a==b){
61         sum+=dis[y]-dis[x];
62         count++;
63     }
64 }

LCA+倍增

但看了题解后,我发现了一个效率更高的方法,即运用时间戳,这样两遍DFS即可解决问题。所谓的时间戳,在本题中就是开两个数组,一个记录每个点先序遍历的先后顺序,另一个则记录每个点后序遍历的先后顺序;如果u是v的祖先,那么u的先序遍历顺序会在v前面,且u的后序遍历顺序会在v后面,利用这一点,即可以马上判断u是否为v的祖先。下面为AC代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #define maxn 233333
 4 struct node{
 5     int to,next,w;
 6 };
 7 node e[maxn];
 8 int n,m,pre[maxn],cnt,first[maxn],last[maxn],con,total,dis[maxn];
 9 long long sum;
10 int read();
11 void dfsf(int);
12 void dfsl(int);
13 void build(int,int,int);
14 int main(){
15     n=read();m=read();cnt=0;
16     for(int i=1;i<n;i++){
17         int a=read(),b=read(),t=read();
18         build(a,b,t);
19     }
20     con=0;dfsf(1);//先序遍历
21     con=0;dfsl(1);//后序遍历
22     sum=0;total=0;
23     for(int i=1;i<=m;i++){
24         int u=read(),v=read();
25         if(first[u]<=first[v]&&last[u]>=last[v]){//判断u是否为v的祖先
26             total++;sum+=dis[v]-dis[u];
27         }
28     }
29     printf("%d\n%d",total,sum);
30     return 0;
31 }
32 int read(){
33     int ans=0,f=1;char c=getchar();
34     while(‘0‘>c||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
35     while(‘0‘<=c&&c<=‘9‘)ans=ans*10+c-48,c=getchar();return ans*f;
36 }
37 void build(int u,int v,int w){
38     e[++cnt].to=v;e[cnt].next=pre[u];pre[u]=cnt;e[cnt].w=w;
39 }
40 void dfsf(int x){
41     first[x]=++con;
42     for(int i=pre[x];i;i=e[i].next){
43         int to=e[i].to;
44         dis[to]=dis[x]+e[i].w;
45         dfsf(to);
46     }
47 }
48 void dfsl(int x){
49     for(int i=pre[x];i;i=e[i].next) dfsl(e[i].to);
50     last[x]=++con;
51 }

时间戳

时间: 2024-10-22 22:20:27

Vijos P1460 拉力赛 倍增+LCA/时间戳的相关文章

Vijos上的三道LCA: 1427, 1460, 1710

Vijos上一共有三道标记为LCA的题目:P1427机密信息,P1460拉力赛,P1710Mrw的工资计划. 首先是P1427机密信息.考虑到只需要求一次LCA,数据范围也不大,直接暴力解决,只是分类讨论有点麻烦. 1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <queue> 6 #define re

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些

【bzoj4242】水壶 BFS+最小生成树+倍增LCA

题目描述 JOI君所居住的IOI市以一年四季都十分炎热著称. IOI市是一个被分成纵H*横W块区域的长方形,每个区域都是建筑物.原野.墙壁之一.建筑物的区域有P个,编号为1...P. JOI君只能进入建筑物与原野,而且每次只能走到相邻的区域中,且不能移动到市外. JOI君因为各种各样的事情,必须在各个建筑物之间往返.虽然建筑物中的冷气设备非常好,但原野上的日光十分强烈,因此在原野上每走过一个区域都需要1单位的水.此外,原野上没有诸如自动售货机.饮水处之类的东西,因此IOI市的市民一般都携带水壶出

Codeforces 418d Big Problems for Organizers [树形dp][倍增lca]

题意: 给你一棵有n个节点的树,树的边权都是1. 有m次询问,每次询问输出树上所有节点离其较近结点距离的最大值. 思路: 1.首先是按照常规树形dp的思路维护一个子树节点中距离该点的最大值son_dis[i],维护非子树节点中距离该点的最大值fa_dis[i]; 2.对于每个节点维护它最大的三个儿子节点的son_dis; 3.维护up[i][j]和down[i][j]数组,这个类似倍增lca里边的fa[i][j],up[i][j]代表的含义是从第j个点向上到它的第2^i个父节点这条链上的点除了该

Tsinsen A1505. 树(张闻涛) 倍增LCA,可持久化线段树,DFS序

题目:http://www.tsinsen.com/A1505 A1505. 树(张闻涛) 时间限制:1.0s   内存限制:512.0MB 总提交次数:196   AC次数:65   平均分:58.62 将本题分享到: 查看未格式化的试题   提交   试题讨论 试题来源 2013中国国家集训队第二次作业 问题描述 给定一棵N个节点的树,每个点有一个权值,有M个询问(a,b,c)若a 为1,回答b到c路径上的最小权值,若a为2,回答b到c路径上的最大权值,若a为3,回答b到c路径上的所有权值的

BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写

【BZOJ2815】【ZJOI2012】灾难 阿米巴和小强题 动态倍增LCA 灾难树

广告: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44104163"); } 题意: 原题面请见JSShining博客 http://www.cnblogs.com/JS-Shining/archive/2013/01/12/2857429.html 题解: 我们构建一颗灾难树,使得一

BZOJ 2791 Poi2012 Rendezvous 倍增LCA

题目大意:给定一棵内向森林,多次给定两个点a和b,求点对(x,y)满足: 1.从a出发走x步和从b出发走y步会到达同一个点 2.在1的基础上如果有多解,那么要求max(x,y)最小 3.在1和2的基础上如果有多解,那么要求min(x,y)最小 4.如果在1.2.3的基础上仍有多解,那么要求x>=y 因此那个x>=y是用来省掉SPJ的,不是题目要求- - 容易发现: 如果a和b不在同一棵内向树上,显然无解,否则一定有解 定义根为从一个点出发能走到的第一个环上点,如果a和b的根相同,则到达LCA是

洛谷P3128 [USACO15DEC]最大流Max Flow [倍增LCA]

题目描述 Farmer John has installed a new system of  pipes to transport milk between the  stalls in his barn (), conveniently numbered . Each pipe connects a pair of stalls, and all stalls are connected to each-other via paths of pipes. FJ is pumping milk