zoukankan      html  css  js  c++  java
  • CF519E A and B and Lecture Rooms

    题目传送门

    这道题目...真是一道好题...题出的好!难度适中!覆盖知识点广!题目有着切合实际的背景!解法比较自然!给出题人点赞 !

    题意:

      给你一棵树,每次询问给出两个点,问树上有多少点到这两个点的距离相等。

    $Sol$


    树上距离...嗯肯定有$LCA$,但是看起来还是毫无思路的样子啊...怎么办。

    这个时候分类讨论一定是个不错的选择(逃)

    由于我只会树上倍增,而且大部分时候它还是很好用的,还能求出许多附属需要的东西,所以我们先用树上倍增法求出树上这两点的距离,进行分类讨论。(设置一个a的深度大于b的深度的前提)

    1)当距离为奇数:无解,答案为0.

    比如答案要求到a和b距离相等的点,ab距离为奇数,显然没有。 

    2)当距离为偶数

     ·  a,b在同一条链上。

    显然b是a,b的LCA,答案就是a.b节点的中点它所有子树的大小除去有a的那棵子树大小。即$size[中点]-size[中点的a所在子树]$

    Explanation:

    (开始在这里理解有偏差,ab中点有可能不止a一个子树)。在ab中点的子树上的点到ab中点的过程中,距离是一定的。而ab中点到a,b距离又分别相等,所以距离相等。

      ·  a,b分别在他们的LCA两侧,且二者的深度相同。

    显然LCA是一个合法的点,而且LCA的祖先们也是合法的点,而且LCA的除a,b所在的所有子树上的所有点也是合法的。(道理同上),答案就是$n-size[lca的a所在子树]-size[lca的b所在子树]$

      ·  a,b分别在他们的LCA两侧,且二者深度不相同。

    有了前面的基础,这里就会好想一点,答案正是$size[mid]-size[mid的子树中有a的那棵]$

    从上面的分析我们也可以看出,我们要求的,也只不过是size,d(深度)。这些在进行倍增预处理时可以顺带求出。然后求“有a的那棵子树”怎么求?我们其实暴力向上倍增一下即可(不是暴力向上跳,是倍增,从大到小缩小范围)。

    Code

      1 #include<cstdio>
      2 #include<algorithm>
      3 #include<queue>
      4 #include<cmath>
      5 #define maxn 100090
      6 
      7 using namespace std;
      8 
      9 int n,m,tot,t;
     10 int head[maxn],size[maxn],d[maxn];
     11 int f[maxn][30];
     12 bool vis[maxn];
     13 struct node{
     14     int to,next;
     15 }edge[maxn*2];
     16 
     17 void add(int x,int y)
     18 {
     19     edge[++tot].to=y;
     20     edge[tot].next=head[x];
     21     head[x]=tot;
     22 }
     23 
     24 void init_1()
     25 {
     26     queue<int>q;
     27     q.push(1);d[1]=1;
     28     while(!q.empty())
     29     {
     30         int u=q.front();q.pop();
     31         for(int i=head[u];i;i=edge[i].next)
     32         {
     33             int v=edge[i].to;
     34             if(d[v]) continue;
     35             d[v]=d[u]+1;
     36             f[v][0]=u;
     37             for(int j=1;j<=t;j++)
     38                 f[v][j]=f[f[v][j-1]][j-1];
     39             q.push(v);
     40         }
     41     }
     42 }
     43 
     44 void init_2(int u)
     45 {
     46     vis[u]=1;size[u]++;
     47     for(int i=head[u];i;i=edge[i].next)
     48     {
     49         int v=edge[i].to;                               
     50         if(vis[v]) continue;
     51         init_2(v);
     52         size[u]+=size[v];
     53     }
     54 }
     55 
     56 int find(int x,int dep)
     57 {
     58     int hu=x;
     59     for(int i=t;i>=0;i--)
     60     {
     61         if(d[hu]-(1<<i)<dep) continue;
     62         hu=f[hu][i];
     63     }
     64     return hu;
     65 }
     66 
     67 int ask(int x,int y)
     68 {
     69     if(d[x]<d[y]) swap(x,y);
     70     int prex=x,prey=y,lca=0,tmp=0;
     71     for(int i=t;i>=0;i--)
     72         if(d[f[x][i]]>=d[y]) x=f[x][i];
     73     if(x==y) lca=x;
     74     else
     75     {
     76         for(int i=t;i>=0;i--)
     77             if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
     78         lca=f[x][0];
     79     }
     80     x=prex,tmp=y,y=prey;
     81     int dis=d[x]+d[y]-2*d[lca];
     82     if(dis&1) return 0;
     83     if(lca==y)
     84     {
     85         int mid=(d[x]+d[y])>>1;
     86         return size[find(x,mid)]-size[find(x,mid+1)];
     87     }
     88     else 
     89     {
     90         if(d[x]==d[y])
     91             return n-size[find(x,d[lca]+1)]-size[find(y,d[lca]+1)];        
     92         else 
     93         {
     94             int mid=d[x]-(dis>>1);
     95             return size[find(x,mid)]-size[find(x,mid+1)];
     96         }
     97     }
     98 }
     99 
    100 int main()
    101 {
    102     scanf("%d",&n);
    103     t=log2(n)+1;
    104     for(int i=1;i<=n-1;i++)
    105     {
    106         int x=0,y=0;
    107         scanf("%d%d",&x,&y);
    108         add(x,y);add(y,x);
    109     }
    110     init_1();
    111     init_2(1);
    112     scanf("%d",&m);
    113     for(int i=1;i<=m;i++)
    114     {
    115         int x=0,y=0;
    116         scanf("%d%d",&x,&y);
    117         if(x==y){printf("%d
    ",n);continue;}
    118         printf("%d
    ",ask(x,y));
    119     }
    120     return 0;
    121 }
    View Code

    参考文献:https://www.cnblogs.com/qixingzhi/p/9302038.html(从这里学会的)

  • 相关阅读:
    Go接口特性
    go递归函数
    K8S部署Redis Cluster集群(三主三从模式) 部署笔记
    Jenkins密码忘记重置方法
    inode满了处理
    Linux 系统日常巡检脚本
    垃圾代码评析——关于《C程序设计伴侣》6.2(三)
    CRITICAL THINGKING笔记——ad hominem
    垃圾代码评析——关于《C程序设计伴侣》6.2(二)
    垃圾代码评析——关于《C程序设计伴侣》6.2(一)
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9722436.html
Copyright © 2011-2022 走看看