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


    拉力赛

    描述

    车展结束后,游乐园决定举办一次盛大的山道拉力赛,平平和韵韵自然也要来参加大赛。

    赛场上共有n个连通的计时点,n-1条赛道(构成了一棵树)。每个计时点的高度都不相同(父结点的高度必然大于子结点),相邻计时点间由赛道相连。由于马力不够,所以韵韵的遥控车只能从高处驶向低处。而且韵韵的车跑完每条赛道都需花费一定的时间。

    举办方共拟举办m个赛段的比赛,每次从第u个计时点到第v个计时点,当然其中有不少比赛韵韵的遥控车是不能参加的(因为要上坡)。平平想知道他能参加多少个赛段的比赛,并且想知道他完成这些赛段的总用时。

    赛道皆为单向。

    格式

    输入格式

    第一行两个整数n,m。

    接下来n-1行每行3个整数a、b、t。

    表示韵韵的遥控车可以花t秒从第a个计时点到第b个计时点。

    接下来m行每行2个整数u、v,意义如描述所示。

    输出格式

    第一行输出一个正整数,表示能参加的赛段数。

    第二行输出一个正整数,表示总用时。

    样例输入

    6 2
    1 2 1
    2 4 1
    2 5 1
    5 6 1
    1 3 1
    2 6
    4 5

    样例输出

    1
    2

    限制

    各个测试点1s

    提示

    第一个计时点的高度是最高的;
    u≠v;
    对于50%的数据 n≤1000 m≤1000;
    对于100%的数据 n≤10000 m≤100000;
    答案小于2^64。

    来源

    f1zsy birdor

    ----------------------------------------------------------------------------------------------------------------------------------------

    因为最近在学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
    %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
    %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 }
    时间戳
  • 相关阅读:
    __name__使用方法,模块查找顺序和模块的绝对导入
    模块
    ATM程序结构
    迭代器
    常用内置方法
    生成式,匿名函数及内置函数部分使用方法
    算法之二分法和三元表达式
    redis配置文件
    PythonStudy——shutil 模块
    PythonStudy——pickle 模块 (泡菜)
  • 原文地址:https://www.cnblogs.com/lpl-bys/p/7383767.html
Copyright © 2020-2023  润新知