【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);
}

 

posted @ 2019-10-15 20:10  HellPix  阅读(163)  评论(0编辑  收藏  举报