zoukankan      html  css  js  c++  java
  • 【noi2013】【bz3244】树的计数

    题目概括:
    给出树的dfs、bfs序 求树的期望高度

    题解:
    由于我比较懒 先copy一段百度文库的题解~
    void copy(){
    我们可以发现,所求的树之所以会有很多种,是因为出现了这种情况:
    对于A、B,A既可以做B的兄弟,又可以做B的父亲。
    (显然其中的一个前提是A、B在dfs、bfs序列中都必须相邻)
    而这样除去A,B的关系外,对于其他任何节点之间的关系都没有任何影响。 所以这中情况对答案的贡献是0.5。
    然后对于A,B,如果A只能是B的父亲(也就是BFS序列中的断层),那么对答案的贡献为1。
    所以,我们只要找出来以上两种关系,最后将贡献值加起来就是答案。
    }
    这个题解木有具体写加0.5的情况 so 下面具体说一下 什么情况加0.5

    我们先把bfs序换成1..n 假设A、B 为i、i+1
    首先 他们的dfs&bfs序必须相邻且位置先后一样(dfs[i]+1==dfs[i+1])
    然后 还要满足以下条件
    1.dfs序<i+1的所有点的bfs序都要<bfs[i+1]
    形象地说就是dfs时在i+1点的前面点的深度不能超过i+1的深度
    证明:
    假设点dfs[j]<dfs[i+1] && bfs[j]>bfs[i+1] 且j的深度==i+1的深度+1
    如果 i+1做了i的儿子 那么i+1就会出现在j的同一层 且在j的后面
    也就是 bfs[j]<bfs[i+1] 矛盾
    2.假设点j为必须断层的点的前一点(该层最后一点)
    对于满足 dfs[i+1]<dfs[x]<dfs[j] 的所有x 必须满足 bfs[x]>=bfs[i+1]
    就是说可能与i+1同层的点 必须是i+1的兄弟 可能不大好理解 简单解释一下
    如果j不是i+1的兄弟 j的老爸的bfs序肯定<bfs[i+1]
    证明:
    条件:bfs[j]>bfs[i+1] 且i+1、j同层
    如果i+1做了i的儿子 那么i+1就会在j的下一层
    也就是 bfs[j]<bfs[i+1] 矛盾

    优化:
    如果每个点都要找到条件2的断层点 就有可能导致n^2的复杂度
    再介绍下由 sto AK大神 提供的小优化
    可以开个临时的变量累加所有的0.5 如果在断层前遇到某个i和i+1不是兄弟 就把临时变量清0
    遇到断层时ans+=临时变量
    还有 判断条件2的时候可以用线段树优化一下 虽然官方的数据卡不掉 但追求完美的AK大神 轻松地构造了一个数据把没线段树的卡了

    代码:

     1 #include <cstdio>
     2 const int NN=200001;
     3 int n,dfs[NN],bfs[NN],rk[NN],max[NN],tree[NN*4];
     4 double ans,save;
     5 inline int minn(int x,int y){ return x<y ? x : y; }
     6 void build(int l,int r,int rt){
     7     if (l==r){
     8         tree[rt]=dfs[l];
     9         return;
    10     }
    11     int mid=(l+r)/2;
    12     build(l,mid,rt*2);
    13     build(mid+1,r,rt*2+1);
    14     tree[rt]=minn(tree[rt*2],tree[rt*2+1]);
    15 }
    16 int min(int l,int r,int rt,int x,int y){
    17     if (x<=l && r<=y) return tree[rt];
    18     int mid=(l+r)/2,res=n;
    19     if (x<=mid) res=minn(res,min(l,mid,rt*2,x,y));
    20     if (mid<y) res=minn(res,min(mid+1,r,rt*2+1,x,y));
    21     return res;
    22 }
    23 int main(){
    24     freopen("bz3244.in","r",stdin);
    25     freopen("bz3244.out","w",stdout);
    26     scanf("%d",&n);
    27     for (int i=1;i<=n;i++) scanf("%d",&dfs[i]);
    28     for (int i=1;i<=n;i++) scanf("%d",&bfs[i]);
    29     for (int i=1;i<=n;i++) rk[bfs[i]]=i;
    30     for (int i=1;i<=n;i++) dfs[i]=rk[dfs[i]];
    31     for (int i=1;i<=n;i++) rk[dfs[i]]=i;
    32     for (int i=1;i<=n;i++) max[i]=max[i-1]>dfs[i] ? max[i-1] : dfs[i];
    33     build(1,n,1);
    34     ans=1;
    35     for (int i=1;i<n;i++){
    36         if (i==1 || rk[i+1]<rk[i]) ans=ans+1+save;
    37         else if (rk[i+1]==rk[i]+1){
    38             if (max[rk[i]]<=i+1) save=save+0.5;
    39         }else if (min(1,n,1,rk[i],rk[i+1])<i) save=0;
    40     }
    41     ans+=save;
    42     printf("%.3f
    ",ans-0.001);
    43     printf("%.3f
    ",ans);
    44     printf("%.3f
    ",ans+0.001);
    45     fclose(stdin);
    46     fclose(stdout);
    47 }
    View Code
  • 相关阅读:
    模拟赛总结
    2018.04.06学习总结
    2018.04.06学习总结
    Java实现 LeetCode 672 灯泡开关 Ⅱ(数学思路问题)
    Java实现 LeetCode 671 二叉树中第二小的节点(遍历树)
    Java实现 LeetCode 671 二叉树中第二小的节点(遍历树)
    Java实现 LeetCode 671 二叉树中第二小的节点(遍历树)
    Java实现 LeetCode 670 最大交换(暴力)
    Java实现 LeetCode 670 最大交换(暴力)
    Java实现 LeetCode 670 最大交换(暴力)
  • 原文地址:https://www.cnblogs.com/g-word/p/3288675.html
Copyright © 2011-2022 走看看