zoukankan      html  css  js  c++  java
  • 在线倍增法求LCA专题

    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<<1];
    int t=0,n,Q,ai,bi,li;
    int p1,p2;
    typedef long long ll;
    ll dis[N]={0};
    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=1;i<=n-1;++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]+1;
                dis[edge[l].v]=dis[k]+edge[l].w;
                father[edge[l].v][0]=k;
                dfs(edge[l].v);
            }
        }
    }
    void pre_chuli()
    {
        for(int j=1;(1<<j)<=n;++j)
          for(int i=1;i<=n;++i)
          if(father[i][j-1]!=-1)
          father[i][j]=father[father[i][j-1]][j-1];
    }
    int lca(int a,int b)
    {
        if(deepp[a]<deepp[b]) swap(a,b);
        int i;
        for(i=0;(1<<i)<=deepp[a];++i);
        i--;
        for(int j=i;j>=0;--j)
          if(deepp[a]-deepp[b]>=(1<<j))
             a=father[a][j];
        if(a==b) return a;
        for(int j=i;j>=0;--j)
          if(father[a][j]!=-1&&father[a][j]!=father[b][j])
          {
            a=father[a][j];
            b=father[b][j];
          }    
        return father[a][0];
    }
    int main()
    {
        freopen("pwalk.in","r",stdin);
        freopen("pwalk.out","w",stdout);
        input();
        memset(father,-1,sizeof(father));
        dis[1]=0;
        deepp[1]=1;
        dfs(1);
        pre_chuli();
        for(int i=1;i<=Q;++i)
        {
            scanf("%d%d",&p1,&p2);
            int ance=lca(p1,p2);
            cout<<dis[p1]+dis[p2]-2*dis[ance]<<endl;
        }
        fclose(stdin);fclose(stdout);
        return 0;
    }

    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

     1 #define N 100100
     2 #include<iostream>
     3 using namespace std;
     4 #include<cstdio>
     5 #include<cstring>
     6 #define Er 20
     7 typedef long long ll;
     8 ll dis[N]={0};
     9 int deepp[N],father[N][Er],t,n,m,u1,v1,w1,Q;
    10 struct Edge{
    11     int v,last,w;
    12 }edge[N<<1];
    13 int head[N];
    14 void add_edge(int u,int v,int w)
    15 {
    16     ++t;
    17     edge[t].v=v;
    18     edge[t].w=w;
    19     edge[t].last=head[u];
    20     head[u]=t;
    21 }
    22 void input()
    23 {
    24     scanf("%d%d",&n,&m);
    25     for(int i=1;i<=m;++i)
    26     {
    27         scanf("%d%d%d",&u1,&v1,&w1);
    28         add_edge(u1,v1,w1);
    29         add_edge(v1,u1,w1);
    30     }
    31 }
    32 void dfs(int k)
    33 {
    34     for(int l=head[k];l;l=edge[l].last)
    35     {
    36         if(!deepp[edge[l].v])
    37         {
    38             deepp[edge[l].v]=deepp[k]+1;
    39             father[edge[l].v][0]=k;
    40             dis[edge[l].v]=dis[k]+edge[l].w;
    41             dfs(edge[l].v);
    42         }
    43     }
    44 }
    45 void pre_chuli()
    46 {
    47     for(int j=1;(1<<j)<=n;++j)
    48       for(int i=1;i<=n;++i)
    49          if(father[i][j-1]!=-1)
    50          father[i][j]=father[father[i][j-1]][j-1];
    51 }
    52 int lca(int a,int b)
    53 {
    54     if(deepp[a]<deepp[b]) swap(a,b);
    55     int i;
    56     for(i=0;(1<<i)<=deepp[a];++i);
    57     i--;
    58     for(int j=i;j>=0;--j)
    59       if(deepp[a]-deepp[b]>=(1<<j))
    60       a=father[a][j];
    61     if(a==b) return a;
    62     for(int j=i;j>=0;--j)
    63       if(father[a][j]!=-1&&father[a][j]!=father[b][j])
    64       {
    65             a=father[a][j];
    66             b=father[b][j];
    67       }
    68     return father[a][0];
    69 }
    70 int main()
    71 {
    72     freopen("ThefallingofZLX.in","r",stdin);
    73     freopen("ThefallingofZLX.out","w",stdout);
    74     input();
    75     memset(father,-1,sizeof(father));
    76     dis[1]=0;
    77     deepp[1]=1;
    78     dfs(1);
    79     pre_chuli();
    80     scanf("%d",&Q);
    81     for(int i=1;i<=Q;++i)
    82     {
    83         scanf("%d%d",&u1,&v1);
    84         int ance=lca(u1,v1);
    85         cout<<dis[u1]+dis[v1]-2*dis[ance]<<endl;
    86     }
    87     fclose(stdin);fclose(stdout);
    88     return 0;
    89 }

    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-1],j-1] f[I,j],表示 i向上2^j的节点
    
    同时,设我们要求的答案g[i,j],即从i向上走到达2^j层的节点所通过路径上的最小值,转移方程
    
    g[I,j]:=min(g[f[I,j-1],j-1],g[I,j-1])
    
    分析一下就是显然的,g[f[I,j-1],j-1]表示的是i向上2^(j-1)的节点到2^j节点间的最小值,g[I,j-1] 是从i向上走到达2^(j-1)层的节点所通过路径上的最小值,两个区间合起来就是当前的区间长度,所以这个方程成立。对于初值,我们可以bfs求深度并记录,与此同时,f[I,0]是父节点,g[I,0]是他向上走一层的边的长度。然后按照倍增算法模板生成f g数组
    
    求解时,用倍增算法模板即可,只不过有几个细节
    
    1、  别忘了给ans赋初值
    
    2、  倍增算法永远是倍增到最近公共祖先的下一层的两个点,所以最后要比较一下ans与那两个点的向上走一步的值
    
    3、  记得这是个森林,要用一个数组记一下那个是父节点,我用的是并查集加bool数组,有个细节是不要直接把fa[i]当I的所在树的根,要get一下,因为并查集的压缩路径是在每次get时才更新,当他fa变了,而他未被查询过,就会导致fa过时了,要重新get一下
    
    4、  在对齐两个所求节点的层数时,别忘了更新答案
    
    大概就是这些,还有相关代码具体实现就不赘述了。
    
    哦,对了,提交不要忘了删除调试用的输出
     1 /*
     2 60分代码:暴力建q棵最大生成树
     3 */
     4 #define N 10008 
     5 #define M 50007
     6 #include<iostream>
     7 using namespace std;
     8 #include<cstdio>
     9 #include<cstring>
    10 #include<algorithm>
    11 int n,m,q,fa[N],head[N],t=0;
    12 struct Edge{
    13     int u,v,w,last;
    14     bool operator <(Edge P)
    15     const{return w>P.w;}
    16 }edge[M<<1];
    17 int read()
    18 {
    19     int ret=0,ff=1;
    20     char s=getchar();
    21     while(s<'0'||s>'9')
    22     {
    23         if(s=='-') ff=-1;
    24         s=getchar();
    25     }
    26     while(s>='0'&&s<='9')
    27     {
    28         ret=ret*10+s-'0';
    29         s=getchar();
    30     }
    31     return ret;
    32 }
    33 inline void add_edge(int u,int v,int w)
    34 {
    35     ++t;
    36     edge[t].u=u;
    37     edge[t].v=v;
    38     edge[t].w=w;
    39     edge[t].last=head[u];
    40     head[u]=t;
    41 }
    42 inline void inpu()
    43 {
    44     n=read();m=read();
    45     int x,y,z;
    46     for(int i=1;i<=m;++i)
    47     {
    48         x=read();y=read();z=read();
    49         add_edge(x,y,z);
    50     }
    51 }
    52 int find(int x)
    53 {
    54     return (fa[x]==x)?x:fa[x]=find(fa[x]);
    55 }
    56 int main()
    57 {
    58     freopen("truck.in","r",stdin);
    59     freopen("truck.out","w",stdout);
    60     inpu();
    61     sort(edge+1,edge+t+1);
    62     q=read();
    63     int x,y;
    64     for(int i=1;i<=q;++i)
    65     {
    66         x=read();y=read();
    67         for(int i=1;i<=n;++i) fa[i]=i;
    68         bool flag=false;
    69         for(int i=1;i<=t;++i)
    70         {
    71             int x1=find(edge[i].u);
    72             int y1=find(edge[i].v);
    73             if(x1!=y1)
    74             {
    75                 fa[y1]=x1;
    76             }
    77             if(find(x)==find(y))
    78             {
    79                  printf("%d
    ",edge[i].w);
    80                  flag=true;
    81                  break;
    82             }
    83         }
    84         if(!flag) printf("-1
    ");
    85     }
    86     fclose(stdin);
    87     fclose(stdout);
    88     return 0;
    89 }
      1 #define N 10010
      2 #define inf 10000800
      3 #include<cstdio>
      4 #define M 50002
      5 #define D 18
      6 #include<cstring>
      7 #include<algorithm>
      8 #include<cstdio>
      9 using namespace std;
     10 int n,m,q,fa[N],t=0;
     11 struct EEdge{
     12     int u,v,w;
     13     bool operator <(EEdge P)
     14     const{return w>P.w;}
     15 }edge1[M];
     16 struct Edge{
     17     int v,w,last;
     18 }e[N];
     19 int dis[N][D+1],fath[N][D+1],dep[N],head[N];
     20 int read()
     21 {
     22     int ret=0,ff=1;
     23     char s=getchar();
     24     while(s<'0'||s>'9')
     25     {
     26         if(s=='-') ff=-1;
     27         s=getchar();
     28     }
     29     while(s>='0'&&s<='9')
     30     {
     31         ret=ret*10+s-'0';
     32         s=getchar();
     33     }
     34     return ret*ff;
     35 }
     36 void inpu()
     37 {
     38     n=read();m=read();
     39     for(int i=1;i<=m;++i)
     40     {
     41         edge1[i].u=read();
     42         edge1[i].v=read();
     43         edge1[i].w=read();
     44     }
     45 }
     46 int fi(int x)
     47 {
     48     return (fa[x]==x)?x:fa[x]=fi(fa[x]);
     49 }
     50 void add_edge(int u,int v,int w)
     51 {
     52     ++t;
     53     e[t].v=v;
     54     e[t].w=w;
     55     e[t].last=head[u];
     56     head[u]=t;
     57 }
     58 void ddkruskal()
     59 {
     60     for(int i=1;i<=n;++i) fa[i]=i;
     61     sort(edge1+1,edge1+m+1);
     62     int tsum=0;
     63     for(int i=1;i<=m;++i)
     64     {
     65         int x1=fi(edge1[i].u);
     66         int x2=fi(edge1[i].v);
     67         if(x1!=x2)
     68         {
     69             tsum++;
     70             fa[x2]=x1;
     71             add_edge(edge1[i].u,edge1[i].v,edge1[i].w);
     72             add_edge(edge1[i].v,edge1[i].u,edge1[i].w);
     73             if(tsum==n-1) break;
     74         }
     75     }
     76 }
     77 void dfs(int k)
     78 {
     79     for(int l=head[k];l;l=e[l].last)
     80     {
     81         if(!dep[e[l].v])
     82         {
     83             dep[e[l].v]=dep[k]+1;
     84             fath[e[l].v][0]=k;
     85             dis[e[l].v][0]=e[l].w;
     86             dfs(e[l].v);
     87         }
     88     }
     89 }
     90 void init()
     91 {
     92     for(int j=1;j<=D;++j)
     93       for(int i=1;i<=n;++i)
     94       {
     95           fath[i][j]=fath[fath[i][j-1]][j-1];
     96           dis[i][j]=min(dis[i][j-1],dis[fath[i][j-1]][j-1]);
     97       }
     98 }
     99 int lca(int a,int b)
    100 {
    101     int ans=inf;
    102     if(dep[a]<dep[b]) swap(a,b);
    103     for(int j=D;j>=0;--j) 
    104     {
    105         if(dep[a]-(1<<j)>=dep[b])
    106         {
    107             ans=min(ans,dis[a][j]);
    108             a=fath[a][j];
    109         }
    110     }
    111     if(a==b) return ans;
    112     for(int j=D;j>=0;--j)
    113     {
    114         if(fath[a][j]!=fath[b][j])
    115         {
    116             ans=min(ans,min(dis[a][j],dis[b][j]));
    117             a=fath[a][j];
    118             b=fath[b][j];
    119         }
    120     }
    121     ans=min(ans,min(dis[a][0],dis[b][0]));
    122     return ans;
    123 }
    124 int main()
    125 {
    126     freopen("truck.in","r",stdin);
    127     freopen("truck.out","w",stdout);
    128     inpu();
    129     ddkruskal();
    130     q=read();
    131     int x,y;
    132     for(int i=1;i<=n;++i)
    133     {
    134         if(!dep[i])
    135         {
    136             dep[i]=1;
    137             fath[i][0]=i;
    138             dis[i][0]=inf;
    139             dfs(i);
    140         }
    141     }
    142     init();
    143     for(int i=1;i<=q;++i)
    144     {
    145         x=read();y=read();
    146         if(fi(x)!=fi(y))  printf("-1
    ");
    147         else printf("%d
    ",lca(x,y));
    148     }
    149     fclose(stdin);
    150     fclose(stdout);
    151     return 0;
    152 }
  • 相关阅读:
    day4-生成器
    第三天-函数
    编码问题
    可变不可变类型总结
    由var与let引起的百度地图所有的覆盖点的信息都是最后一个的
    《企业应用架构模式》 三
    IndexDB
    ESA与SOA
    SOA
    Dubbo(一)
  • 原文地址:https://www.cnblogs.com/c1299401227/p/5621554.html
Copyright © 2011-2022 走看看