bzoj3244 NOI2013树的计数 神奇脑洞题+线段树

来源:互联网 发布:国家要打仗了知乎 编辑:程序博客网 时间:2024/06/05 12:55

题意:给你两串序列,分别为dfs序和bfs序,让你求树的期望高度。
这题脑洞是真的大。。说实话我都不知道这道题的tag应该是什么。。
copy一下百度文库的题解
void copy(){
我们可以发现,所求的树之所以会有很多种,是因为出现了这种情况:
对于A、B,A既可以做B的兄弟,又可以做B的父亲。
(显然其中的一个前提是A、B在dfs、bfs序列中都必须相邻)
而这样除去A,B的关系外,对于其他任何节点之间的关系都没有任何影响。 所以这中情况对答案的贡献是0.5。
然后对于A,B,如果A只能是B的父亲(也就是BFS序列中的断层),那么对答案的贡献为1。
所以,我们只要找出来以上两种关系,最后将贡献值加起来就是答案。
}
所以我们发现,可以通过O(N)来累计树上的答案。
首先设A,B为i,i+1
贡献为1时:
i=1(根的高度)或者A只能是B的父亲(也就是BFS序列中的断层)
写成表达式就是:(bfs[i]

uses math;type node=record    l,r,val:longint;end;var    tr:array[0..800000]of node;    a,b,dfs,bfs,num,ls,rs:array[0..800000]of longint;    ans,ans1:extended;    i,j,n,m,dfn:longint;procedure build(l,r:longint);var    i,j,m:longint;        x:longint;begin    inc(dfn);        x:=dfn;    tr[x].l:=l;    tr[x].r:=r;    if l=r then    begin        tr[x].val:=a[l];        exit;    end;    m:=(l+r)div 2;    ls[x]:=dfn+1;        build(l,m);    //build(x+x,l,m);    rs[x]:=dfn+1;        build(m+1,r);    //build(x+x+1,m+1,r);    tr[x].val:=min(tr[ls[x]].val,tr[rs[x]].val);end;function find(x,l,r:longint):longint;var    m:longint;begin    if (tr[x].l>=l)and(tr[x].r<=r)then exit(tr[x].val);    m:=(tr[x].l+tr[x].r)div 2;    find:=n;    if l<=m then find:=min(find(ls[x],l,r),find);    if r>m then find:=min(find(rs[x],l,r),find);end;begin    readln(n);    for i:=1 to n do read(a[i]);    for i:=1 to n do read(b[i]);    for i:=1 to n do bfs[b[i]]:=i;    for i:=1 to n do a[i]:=bfs[a[i]];    for i:=1 to n do    begin        dfs[a[i]]:=i;        if a[i]>num[i-1] then num[i]:=a[i]        else num[i]:=num[i-1];    end;    ans:=1;    ans1:=0;    dfn:=0;    build(1,n);    for i:=1 to n-1 do    begin        if (i=1)or(dfs[i]>dfs[i+1])then ans:=ans+ans1+1        else if dfs[i+1]=dfs[i]+1 then        begin            if num[dfs[i]]<=i+1 then            ans1:=ans1+0.5;        end        else if find(1,dfs[i],dfs[i+1])<i then ans1:=0;    end;        ans:=ans+ans1;    writeln(ans:0:3);end.
0 0