• acm 2015北京网络赛 F Couple Trees 树链剖分+主席树


    Couple Trees

    Time Limit: 1 Sec  

    Memory Limit: 256 MB

    题目连接

    http://hihocoder.com/problemset/problem/1232

    Description

    "Couple Trees" are two trees, a husband tree and a wife tree. They are named because they look like a couple leaning on each other. They share a same root, and their branches are intertwined. In China, many lovers go to the couple trees. Under the trees, lovers wish to be accompanied by a lifetime.

    Ada and her boyfriend Asa came to the couple trees as well. They were very interested in the trees. They were all ACMers, so after careful observation, they found out that these two trees could be considered as two "trees" in graph theory. These two trees shared N vertices which were labeled 1 to N, and they all had exactly N vertices. Vertices 1 was the root of both trees.

    Ada and Asa wanted to know more about the trees' rough bark, so each of them put one thumb at a vertices. Then they moved their thumbs towards the root. Ada moved along the wife tree, and Asa moved along the husband tree. Of course, they could moved at different speed.

    At that moment, a thought suddenly came to Ada's mind: their thumbs may meet before the root. Which one was the earliest possible meeting vertex? And how many vertices would Ada and Asa encounter on the way to the meeting vertex?

    Input

    The input consists of no more than 8 test cases.

    For each test case:

    The first line contains two integers, N and M, indicating the number of vertices and the number of queries.(1≤N,M≤100,000)

    The next line contains N−1 integers. It describes the structure of wife tree in this way: If the ith integer is k, it means that the vertex labeled k is the father vertex of the vertex labeled (i+1) . It's guaranteed that a vertex X's father vertex can't have a larger label than X does.

    The next line describes the husband tree in the same way.

    Then next M lines describe the queries. Each line contains two integers Xi and Yi. Let Ki be the earliest possible meeting vertex of the ith query (K0 is defined as 0). In the ith query, Ada's thumb was put at the vertex labeled (Xi+Ki−1) mod N + 1 and Asa's thumb was put at the vertex labeled (Yi+Ki−1) mod N + 1.(1≤Xi,Yi≤N) at the beginning.

    Output

    For each test case:

    Output the answer for each query in a single line. The answer contains three integers: the earliest possible meeting vertex, the number of the vertices Ada will encounter and the number of the vertices Asa will encounter (including the starting vertex and the ending vertex). In particular, if they put their thumb at the same vertex at first, the earliest possible meeting vertex should be the starting vertex.

    Sample Input

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

    Sample Output

    3 2 2
    1 1 3
    1 2 1
    2 2 1
    1 2 2
    3 1 1
    2 1 2

    HINT

    题意

    给你两棵树,都同时往上爬,问你这两个人都能够经过的点中,最大的点是什么,并且都各走了多少步。

    注意父节点的编号一定比子节点小。

    题解:

    本来是想做做倍增的题,然后不小心搜到了这道。想了好久还是不会,看了题解才明白,原来倍增是在优化暴力,但是竟然比正解快!

    1.那我们先来讲讲倍增的具体做法吧。

    先想想最暴力的方法:就是每次找编号较大的一个的点往上爬,爬到超过另一个点,再换这个点重复这个操作。不难证明如果他们在中途相遇就是我们要找的答案。

    但是一个一个往上爬是不是太慢了,确实,那我们就用倍增加速即可。

    2.我们怎么能满足于此呢,正解其实是树链剖分+主席树

    我一开始看题解也是很懵逼,看了好多遍,都没看懂,于是直接看代码,终于明白题解的意思了,真是太神奇了。

    我先讲讲具体做法,之后再分析怎么一步一步想出来的。

    (1)我们把第一棵树剖成链,第二棵树构造主席树。第二颗树每个结点都是一颗一棵线段树,每个结点形成线段树包含这个点到根结点的某些信息,合起来就叫做主席树。

    (2)那么主席数需要包含什么信息呢?这个我还真不太好形象地解释。

        不如我先来说说数剖的作用吧:树剖即把一条链变成log段连续的序列(要不断告诉自己是dfs序连续,所以我们要利用dfs序列)。

        所以我们主席树维护的是树1的dfs序列,也就是树2的某个点i,我们令rk[i]位置的值(即i的dfs序)为i。这样树1的每一段x,top[x],只要在主席树里求rk[top[x]]~rk[x]的最大值即可。

    (3)这么说如果不会可能还是不会,只用理解一下大概思路就行,代码很容易看懂,配合代码理解感觉更轻松。

    最后如果你想研究的透彻一点的话,可以看看接下来的分析。先来把模型简化一下:

    问题1.给你两个序列a和b,每次询问给你一个数j,求最小的k1使得a[k1]=b[j+k2](k2>=0)

    问题2.询问改成两个数i,j,求最小的k1使得a[i+k1]=b[j+k2]

    第一问我们可以把数组a重新编号为{1,2,3,4,...,n},也就是原本的a[x]变成x,然后b数组也是同样变化,即 if (b[y]=a[x])b[y]=x。这样只用在b数组[j,n]区间里求最小值即可。

    第二问,那么就不是简单地求最小值了,我们要求大于等于等于i的最小值(注意此时,a数组已经变成{1,2,3,..n}了,b数组也相应的变了)。

    时刻说出自己要求的东西也可以帮我们理清思路:

    (1)现在我们已经不用管第一个序列了,因为他是一个公差为1的等差数列

    (2)我们要求数组二 b[j,n]中的大于i的最小值,求出i在b[j,n]中的排名,也就得到了我们想要的答案。

    我都说到这个份上了,应该能想到主席树了。如果没想到请赶快记下主席树的两大板子:

    (1)区间第k大

    (2)区间k的排名(这个就是我们的题目要用的)

    如果你真正理解了上面两个问题,那么两棵数和两个序列其实殊途同归,树链剖分dfs序列其实就是把数组a变成等差数列的过程,二且因为父节点一定小于子节点,所以k1最小等价于k2最小。

     

    代码:

    lca优化暴力(依靠数据水)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 #define N 200050
     5 int n,m;
     6 template<typename T>void read(T&x)
     7 {
     8   ll k=0; char c=getchar();
     9   x=0;
    10   while(!isdigit(c)&&c!=EOF)k^=c=='-',c=getchar();
    11   if (c==EOF)exit(0);
    12   while(isdigit(c))x=x*10+c-'0',c=getchar();
    13   x=k?-x:x;
    14 }
    15 void read_char(char &c)
    16 {while(!isalpha(c=getchar())&&c!=EOF);}
    17 struct Tree
    18 {
    19   int fa[N][20],dp[N];
    20   void init()
    21     {
    22       dp[1]=1;
    23       for(int i=2;i<=n;i++)
    24     {
    25       read(fa[i][0]);
    26       dp[i]=dp[fa[i][0]]+1;
    27     }
    28       for(int i=1;i<=19;i++)
    29     for(int j=1;j<=n;j++)
    30       fa[j][i]=fa[fa[j][i-1]][i-1];
    31     }
    32 }G[3];
    33 int get_ans(int x,int y)
    34   {
    35     int k=1;
    36     while (x!=y)
    37       {
    38     if (x<y)swap(x,y),k=3-k;
    39     for(int i=19;i>=0;i--)
    40       if (G[k].fa[x][i]>y)x=G[k].fa[x][i];
    41     x=G[k].fa[x][0];
    42     if (x==y)return x;
    43       }
    44     return x;
    45   }
    46 void work()
    47   {
    48     read(n); read(m);
    49     G[1].init(); G[2].init();
    50     int ans=0;
    51     for(int i=1;i<=m;i++)
    52       {
    53     int x,y,t1,t2;
    54     read(x); read(y);
    55     x=(x+ans)%n+1;
    56     y=(y+ans)%n+1;
    57     ans=get_ans(x,y);
    58     t1=G[1].dp[x]-G[1].dp[ans]+1;
    59     t2=G[2].dp[y]-G[2].dp[ans]+1;
    60     printf("%d %d %d
    ",ans,t1,t2);
    61       }
    62   }
    63 int main()
    64 {
    65 #ifndef ONLINE_JUDGE
    66   freopen("aa.in","r",stdin);
    67 #endif
    68   while(1)
    69     {
    70       work();
    71     }
    72 }
    View Code

    树链剖分+主席树

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define ll long long
      4 #define N 100050
      5 int n,m,dp2[N];
      6 int root[N],TreeNum;
      7 int last[N],tot;
      8 int cnt,fa[N],dp[N],size[N],son[N],rk[N],kth[N],top[N];
      9 struct Tree{int l,r,ls,rs,max;}tr[N*20];
     10 struct Edge{int from,to,s;}edges[N<<1];
     11 template<typename T>void read(T&x)
     12 {
     13   ll k=0; char c=getchar();
     14   x=0;
     15   while(!isdigit(c)&&c!=EOF)k^=c=='-',c=getchar();
     16   if (c==EOF)exit(0);
     17   while(isdigit(c))x=x*10+c-'0',c=getchar();
     18   x=k?-x:x;
     19 }
     20 void read_char(char &c)
     21 {while(!isalpha(c=getchar())&&c!=EOF);}
     22 void AddEdge(int x,int y)
     23 {
     24   edges[++tot]=Edge{x,y,last[x]};
     25   last[x]=tot;
     26 }
     27 void dfs1(int x,int pre)
     28 {
     29   fa[x]=pre;
     30   dp[x]=dp[pre]+1;
     31   size[x]=1;
     32   son[x]=0;
     33   for(int i=last[x];i;i=edges[i].s)
     34     {
     35       Edge &e=edges[i];
     36       if (e.to==pre)continue;
     37       dfs1(e.to,x);
     38       size[x]+=size[e.to];
     39       if(size[e.to]>size[son[x]])son[x]=e.to;
     40     }
     41 }
     42 void dfs2(int x,int y)
     43 {
     44   rk[x]=++cnt;
     45   kth[cnt]=x;
     46   top[x]=y;
     47   if (son[x]==0)return;
     48   dfs2(son[x],y);
     49   for(int i=last[x];i;i=edges[i].s)
     50     {
     51       Edge &e=edges[i];
     52       if (e.to==son[x]||e.to==fa[x])continue;
     53       dfs2(e.to,e.to);
     54     }
     55 }
     56 void bt(int &x,int l,int r)
     57 {
     58   x=++TreeNum;
     59   tr[x].l=l ;tr[x].r=r; tr[x].max=0;
     60   if (l==r)return;
     61   int mid=(l+r)>>1;
     62   bt(tr[x].ls,l,mid);
     63   bt(tr[x].rs,mid+1,r);
     64 }
     65 void add(int &x,int last,int p,int tt)
     66 {
     67   x=++TreeNum;
     68   tr[x]=tr[last];
     69   tr[x].max=max(tr[x].max,tt);
     70   if (tr[x].l==tr[x].r)return;
     71   int mid=(tr[x].l+tr[x].r)>>1;
     72   if (p<=mid)add(tr[x].ls,tr[last].ls,p,tt);
     73   else add(tr[x].rs,tr[last].rs,p,tt);
     74 }
     75 int query(int x,int l,int r)
     76 {
     77   if (l<=tr[x].l&&tr[x].r<=r)
     78     return tr[x].max;
     79   int mid=(tr[x].l+tr[x].r)>>1,a1=0,a2=0;
     80   if (l<=mid)a1=query(tr[x].ls,l,r);
     81   if (mid<r)a2=query(tr[x].rs,l,r);
     82   return max(a1,a2);
     83 }
     84 int get_ans(int x,int y)
     85 {
     86   int fx=x,tp,ans=0;
     87   while(x)
     88     {
     89       tp=query(root[y],rk[fx],rk[x]);
     90       ans=max(ans,tp);
     91       x=fa[fx];fx=top[x];
     92     }
     93   return ans;
     94 }
     95 void work()
     96 {
     97   read(n); read(m);
     98   for(int i=2;i<=n;i++)
     99     {
    100       read(fa[i]);
    101       AddEdge(fa[i],i);
    102     }
    103   dfs1(1,0);
    104   dfs2(1,1);
    105   dp2[1]=1;
    106   bt(root[0],1,n);
    107   add(root[1],root[0],1,1);
    108   for(int i=2;i<=n;i++)
    109     {
    110       int y;
    111       read(y);
    112       dp2[i]=dp2[y]+1;
    113       add(root[i],root[y],rk[i],i);
    114     }
    115   int ans=0;
    116   for(int i=1;i<=m;i++)
    117     {
    118       int x,y,t1,t2;
    119       read(x); read(y);
    120       x=(x+ans)%n+1; y=(y+ans)%n+1;
    121       ans=get_ans(x,y);
    122       t1=dp[x]-dp[ans]+1;
    123       t2=dp2[y]-dp2[ans]+1;
    124       printf("%d %d %d
    ",ans,t1,t2);
    125     }
    126 }
    127 void clear()
    128 {
    129   tot=0; cnt=0; TreeNum=0;
    130   memset(last,0,sizeof(last));
    131 }
    132 int main()
    133 {
    134 #ifndef ONLINE_JUDGE
    135   freopen("aa.in","r",stdin);
    136 #endif
    137   while(1)
    138     {
    139       clear();
    140       work();
    141     }
    142 }
    View Code
  • 相关阅读:
    Windows7 共享文件夹的两个BUG
    POJ 1845 Sumdiv(数论,求A^B的所有约数和)
    POJ 2481 Cows(树状数组)
    HDU 1124 Factorial(简单数论)
    POJ 1195 Mobile phones(二维树状数组)
    POJ 3067 Japan(树状数组求逆序对)
    HDU 4027 Can you answer these queries?(线段树)
    HDU 1576 A/B(数论简单题,求逆元)
    HDU 1166 敌兵布阵(线段树,树状数组)
    ZOJ 1610 Count the Colors(线段树)
  • 原文地址:https://www.cnblogs.com/mmmqqdd/p/10810216.html
Copyright © 2020-2023  润新知