NOI2013 樹的計數

NOI2013 樹的計數

給定一個dfs序和bfs序,求解符合這兩個條件的所有樹的平均樹高。

思路

如果我們能夠給bfs序中每一段區間分層,然後再去對應dfs序,不難發現可以唯一確定一棵樹,即在dfs的過程中,前後兩個節點的關係可以按照層數來判斷,分爲兒子或者是某一層祖先的另外一個兒子。

而給bfs序分層的時候我們需要滿足一些限制:

  1. bfs序中同一層在dfs中訪問順序必須嚴格按照bfs的訪問順序來。
  2. dfs中每一次只能往下走一層或者是往上走若干層或者還停留在這一層。
  3. 1號點必須要分層。

接下來考慮怎麼求解樹高,bfs按照限制分完層之後會有一些斷點,我們可以求出在滿足限制的情況下每一個點成爲端點的期望是多少,最後根據期望的線性性直接相加就可以得到最後的答案。

考慮如何去滿足這些限制。

從1號限制的逆否命題可以退出,如果相鄰的兩個點在dfs中沒有按照編號的順序來,那麼它們一定不在同一層,即pos[i]>pos[i+1],那麼可以肯定i一定是斷點,不難發現滿足了這個限制就滿足了1號限制。

  1. 在dfs序中相鄰的兩個點d[i],d[i+1],如果d[i+1] < d[i],那麼i+1一定是i某一個祖先的另一個兒子,它們之間的斷點個數是可以任意的。

  2. 如果d[i+1]>d[i]+1,它們一定不是兄弟的關係,也就是這兩個點只能做父子,並且它們之間的斷點個數一定爲1,也就是我們可以標記這中間所有的點,它們都是已經確定的。

  3. 如果d[i+1]=d[i]+1,它們既可以做父子又可以做兄弟,它們之間的關係也是任意的。

但是上面1.3情況的任意取值都受2情況的影響,我們標記所有2情況影響的點,然後對於剩下的沒有標記的點就可以任意取值,期望爲0.5。

最後的答案就是確定爲1的點加上期望爲0.5的點。

// luogu-judger-enable-o2
/*=======================================
 * Author : ylsoi
 * Time : 2019.6.18
 * Problem : luogu1232
 * E-mail : [email protected]
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("luogu1232.in","r",stdin);
    freopen("luogu1232.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

string proc(){
    ifstream f("/proc/self/status");
    return string(istreambuf_iterator<char>(f),istreambuf_iterator<char>());
}

const int maxn=2e5+10;
int n,b[maxn],d[maxn],pb[maxn],pd[maxn],vis[maxn];
double ans;

int main(){
    //File();

    read(n);
    REP(i,1,n)read(d[i]);
    REP(i,1,n)read(b[i]),pb[b[i]]=i;
    REP(i,1,n)d[i]=pb[d[i]],pd[d[i]]=i;
    REP(i,2,n-1){
        if(pd[i]>pd[i+1])ans+=1,++vis[i],--vis[i+1];
        if(d[i]+1<d[i+1])++vis[d[i]],--vis[d[i+1]];
    }
    REP(i,3,n)vis[i]+=vis[i-1];
    REP(i,2,n-1)if(!vis[i])ans+=0.5;
    printf("%.3lf\n",ans+2);

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章