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

    题目

    https://www.luogu.org/problem/P1232

    题意简述

    给定一棵树的$dfs$序和$bfs$序,求它深度的期望。

    题解

    首先,显然的,以$bfs$序为自然排列,对$dfs$序做一次置换。

    其次,我们知道,要对$bfs$序做划分,连续的一组代表这些节点的深度都是一样的,组和组之间的深度是递增的。

    我们知道,如果我们做好了划分,一定能确定树的形态,这里给出一种方法:

    1.首先$1$号节点肯定单独一组,子树是$dfs$序上$i in [1..n]$的$dfn[i]$。
    2.再找$2,3..$号节点,既然它们在一组,每个点找下一个点出现的位置,从它的位置到下一个点出现的位置即子树的区间。
    3.以此类推。

    这样划分一定是可以构造出来这棵树的。

    但是当这个构造方法不能奏效时,就无法构造这棵树。有两种情况:

    1.对于同组之间的元素,$bfs$序上,同一组的数的位置没有递增(即子树范围是负的)
    2.对于不同组之间的元素,任意$dfs$序上连续的两点,它们之间要么是父-子关系,要么先上,再向下一步,即$dep[dfn[i]]+1 ge dep[dfn[i+1]]$。

    设$x_i$表示$i$和$i+1$间是否有划分。我们求的就是$sum_{i=1}^{n-1}{x_i}$的期望,有

    1.$x_1=1$
    2.设$dfn[p_x]=x$,若$p_x>p_{x+1}$,则$x_i=1$(限制$1$的应用)
    3.若$dfn[k]<dfn[k+1]$,则$sum_{i=dfn[k]}^{dfn[k+1]-1}{x_i} le 1$

    确定答案的话,因为这是一个差分约束的式子,我们可以用模拟松弛操作来更新它,最后我们一定可以确定一些$x_i=1$,一些$x_i=0$,对于无法确定的,因为独立,我们直接把它的值设为$0.5$即可。

    小结

    这道题我认为是一道很难的题,因为不好找到切入点,我在思考的时候是考虑$dfs$序上连续的两点,它们的深度即$bfs$序大小的关系,这样做一些细节是无法考虑到的,换句话说,只考虑局部之间的关系,没有考虑整体的限制。之前也设想过划分$bfs$序的做法,事实上,这种做法是及其优美的,深度这个看似需要最大值的东西被转化成了序列上的元素和,如果能先想到这个优美的转化,不考虑构造答案的情况下,逆向思维看待这个问题大概是能想出来的。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ri register int
    #define N 200050
    using namespace std;
    
    int dfn[N],bfn[N],p[N],dc[N];
    int n;
    
    inline int read() {
      int ret=0,f=0; char ch=getchar();
      while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar();
      while (ch>='0' && ch<='9') ret*=10,ret+=(ch-'0'),ch=getchar();
      return f?-ret:ret;
    }
    
    int main() {
      n=read();
      for (ri i=1;i<=n;i++) dfn[i]=read();
      for (ri i=1;i<=n;i++) bfn[read()]=i;
      for (ri i=1;i<=n;i++) {
        dfn[i]=bfn[dfn[i]];
        p[dfn[i]]=i;
      }
      dc[1]++; dc[2]--; 
      double ans=2;
      for (ri i=1;i<n;i++) if (p[i]>p[i+1]) {
        ans++;
        dc[i]++; dc[i+1]--;
      }
      for (ri i=1;i<n;i++) if (dfn[i]<dfn[i+1]-1) {
        dc[dfn[i]]++; dc[dfn[i+1]]--;
      }
      for (ri i=1,w=0;i<n;i++) {
        w+=dc[i];
        if (!w) ans+=0.5;
      }
      printf("%.3lf",ans);
    }
  • 相关阅读:
    qt教程
    linux shell 教程
    CMakeList.txt学习
    tx2上直接编译带contrib cuda的opencv
    tx2 opencv交叉编译后的对应文件的放置位置
    opencv4.1.0 交叉编译遇到的问题
    docker 学习
    c++ 类注意点
    数据库整理(五)数据库编程 触发器 事务 过程
    数据库整理(四)数据库安全性与完整性
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11680038.html
Copyright © 2011-2022 走看看