1. 程式人生 > >Vijos P1460 拉力賽 倍增+LCA/時間戳

Vijos P1460 拉力賽 倍增+LCA/時間戳

-a get 個數 opened bsp 先後 pri void lld

因為最近在學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/時間戳