• CF842E Nikita and game


    题意:每次在树上加一个点,问树上有多少个起点存在与另一点的距离=直径长?

    标程:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N=300005;
     4 int dep[N],n,fa[N][22],d1,d2,len;
     5 set<int> s1,s2;
     6 set<int> ::iterator t;
     7 int lca(int x,int y)
     8 {
     9     if (dep[x]<dep[y]) swap(x,y); 
    10     int del=dep[x]-dep[y];
    11     for (int i=20;i>=0;i--)
    12       if (del&(1<<i)) x=fa[x][i];
    13     if (x==y) return x;
    14     for (int i=20;i>=0;i--)
    15       if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    16     return fa[x][0];
    17 }
    18 int dis(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}
    19 void init()
    20 {
    21     for (int j=1;j<=20;j++)//预处理正循环 
    22       for (int i=2;i<=n+1;i++)
    23         fa[i][j]=fa[fa[i][j-1]][j-1];
    24 }
    25 int main()
    26 {
    27     scanf("%d",&n);
    28     for (int i=2;i<=n+1;i++) scanf("%d",&fa[i][0]),dep[i]=dep[fa[i][0]]+1;
    29    init(); s1.insert(1);
    30     for (int i=2;i<=n+1;i++)
    31     {
    32        d1=(int)s1.size()==0?0:dis(i,*s1.begin());
    33        d2=(int)s2.size()==0?0:dis(i,*s2.begin());
    34        if (max(d1,d2)>len)
    35        {
    36            if (d1>len)
    37            {
    38                for (t=s2.begin();t!=s2.end();++t)
    39                  if (dis(i,*t)==d1) s1.insert(*t);
    40               s2.clear();    s2.insert(i);
    41             }else {
    42                 for (t=s1.begin();t!=s1.end();++t)
    43                   if (dis(i,*t)==d2) s2.insert(*t);
    44                 s1.clear(); s1.insert(i);
    45             }
    46            len=max(d1,d2);
    47         }else if (max(d1,d2)==len)
    48         {
    49             if (d1==len) s2.insert(i);else s1.insert(i);
    50         }
    51        printf("%d
    ",(int)s1.size()+(int)s2.size());    
    52     }
    53     return 0;
    54 }

    易错点:1.lca_init的时候j应该正着枚举。

    2.所有直径一定会相交,直径的中点(中心)一定被经过。

    题解:set+性质

    固定根,直径由离根最远的点的集合s1以及离s1中点最远的点的集合s2中的点组成。

    因此维护两个set(vector?),由新加入的点到s1中任意一点的距离和s2中任意一点的距离来判断该点的去向。(可以证明取集合中的任意一点不会影响距离>=原直径的情况)

    如果距离>原直径,那么更新此点为s1或s2中的唯一端点,并用被清空的集合更新另一个集合(可以证明有用的端点集合包含于之前的直径端点集合)。

    如果距离=原直径,加入s1或s2。

    时间复杂度O(nlogn),似乎并不需要set。

  • 相关阅读:
    C#位操作符
    NGEN 本机映像生成器 【转载】
    Azure Services Platform
    补补算术基础:编程中的进制问题
    泛型约束
    去除Live Messenger 中的广告
    对代码性能进行调试和量测
    几个常用的文档转换工具(Office System)
    LINQ to DataSet
    使用 Entity Framework 實現彈性的資料模型 【转载】
  • 原文地址:https://www.cnblogs.com/Scx117/p/9081691.html
Copyright © 2020-2023  润新知