码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2017-08-17 20:10:09      阅读:168      评论:0      收藏:0      [点我收藏+]

标签:style   数组   for   int   ++   记录   方法   void   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 }
时间戳

 

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

标签:style   数组   for   int   ++   记录   方法   void   lca   

原文地址:http://www.cnblogs.com/lpl-bys/p/7383767.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!