日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

BZOJ 3244: [Noi2013]树的计数

發布時間:2024/9/5 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BZOJ 3244: [Noi2013]树的计数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

傳送門

神仙題...

和樹的深度有關,由于 $BFS$ 序的性質,顯然可以通過把 $BFS$ 序分成若干段來求出深度,每一段就對應某一深度從左到右的所有節點,那么如果確定了分的段數就確定了樹的深度(分的段數 $+1$)

為了方便,先把 $BFS$ 序變成從 $1$ 到 $n$ 的序列, $DFS$ 序當然也要隨著變一下

考慮每個位置是否可以分段,無非 $3$ 種情況:

$1.$ 此位置必須分,那么對樹的深度貢獻就是 $1$

$2.$ 此位置不能分,那么貢獻就是 $0$

$3.$ 此位置可分可不分,那么貢獻就是 $0.5$(此位置分的樹的方案數和不分的樹的方案數是一樣的,如果分貢獻 $1$,不分貢獻 $0$,那么平均貢獻就是 $0.5$)

考慮用 $BFS$ 序和 $DFS$ 序之間的關系求出每個位置的限制,設節點 $i$ 的 $BFS$ 序為 $bfn[i]$,$DFS$ 序為 $dfn[i]$

?

對于 $BFS$ 序連續的兩點 $x,y=x+1$(此時已經按 $bfn$ 重新標號了),如果 $dfn[x]>dfn[y]$ ,說明 $y$ 在 $x$ 的下一層

大概圖長這樣:

可以發現只有這種情況才會出現 $dfn[x]>dfn[y]$ 的情況,因為如果 $x$ 和 $y$ 在同一層那么顯然 $y$ 會更晚被 $dfs$ 到

所以如果 $dfn[x]>dfn[x+1]$,那么 $x$ 和 $x+1$ 之間必須分層

那么如果 $dfn[x]<dfn[x+1]$ ,是否意味著 $x$ 和 $x+1$ 之間不能分層呢,顯然是不一定的,對于這種情況我們仍然無法確定是否要分層

可能有兩種情況,$x$ 沒有兒子, $x+1$ 是 $x$ 的兄弟;$x+1$ 是 $x$ 的兒子,還是沒辦法確定,而且發現不管是哪種情況都不會對之后的序列產生影響,這保證了無法確定的位置貢獻是 $0.5$(這個位置不管切不切方案數都是一樣的)

?

對于 $DFS$ 序連續的兩點 $x,y$,情況比較多

如果 $x>y-1$ ($bfn[x]>bfn[y-1]$),那么說明 $y$ 是 $x$ 某個祖先的兒子:

對限制并沒有影響

如果 $x==y-1$,那么 $y$ 作為 $x$ 兄弟或者兒子都是合法的

所以仍然無法確定貢獻,但是同樣可以發現不管是哪種情況對后面的序列都沒有影響(里后面節點可以接到 $y$ 下面或者作為 $y$ 的下一個兄弟)

但是第三種情況有點意思:$x<y-1$

說明 $y$ 一定是 $x$ 的第一個兒子:

?

那么就是說,編號從 $x$ 到 $y-1$ 的所有節點深度差不超過1

而且可以發現,如果有分割那么在之前就被計算過了(一定存在 $k$ 使得 $dfn[x+k]>dfn[x+k+1]$ , $x+k \in [x+1,y-1]$ )

所以對于 $x<y-1$ 的情況,區間 $[x,y)$ 的位置都不能產生貢獻

這個限制可以用一個差分數組維護

最后剩下的都不能確定了,貢獻就是 $0.5$

最后一個問題,這些限制是必要的,但是,充分嗎?

感性理解一下,很充分,如果懷疑的話打個暴力拍它幾個小時,發現沒問題,所以是充分的...

并不會證明限制充分 $qwq$

代碼很短,但是并不好想...

設 $pos[i]$ 表示 $dfn$ 為 $i$ 的節點(其實就是 $dfn$ 的反數組)

那么對于前面最后一個限制,$x<y-1$,其實就是 $pos[i]<pos[i+1]-1$

注意一開始那些一定要分割的位置已經算過就不會再產生 $0.5$ 的貢獻了

還有第一個節點一定單獨要分一層

#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; inline int read() {int x=0,f=1; char ch=getchar();while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }return x*f; } const int N=2e6+7; int n; double ans; int dfn[N],pos[N],sum[N];//sum是差分數組 //dfn是節點的dfs序,pos是dfn的反數組,所以有 pos[dfn[i]]=i,dfn[pos[i]]=i inline void mark(int x,int y) { sum[x]++,sum[y+1]--; }//打差分標記 int main() {// freopen("1.in","r",stdin);n=read();ans=1; mark(1,1);//第一個節點一定要分一層for(int i=1;i<=n;i++) dfn[read()]=i;//讀入初始dfs序for(int i=1;i<=n;i++) pos[dfn[read()]]=i;//用初始dfs序更新變化后的pos//上面一行不太好想。因為bfs序重標號了,原來的節點read()就變成了i,所以pos[dfn[read()]]=ifor(int i=1;i<=n;i++) dfn[pos[i]]=i;//用更新后的pos更新dfnfor(int i=1;i<n;i++)//注意i不取n {if(dfn[i]>dfn[i+1]) ans++,mark(i,i);//注意這里也要打標記,之后不會再產生0.5的貢獻if(pos[i]<pos[i+1]-1) mark(pos[i],pos[i+1]-1);//打標記 }int now=0;for(int i=1;i<n;i++) now+=sum[i],ans+=(now ? 0 : 0.5);//如果這個位置不確定則貢獻為0.5//注意上一行i不取n,因為切的位置只有n-1個ans+=1;//深度等于分的段數+1printf("%.3lf\n%.3lf\n%.3lf",ans-0.001,ans,ans+0.001);//BZOJ上要這樣輸出...return 0; }

?

轉載于:https://www.cnblogs.com/LLTYYC/p/10818845.html

總結

以上是生活随笔為你收集整理的BZOJ 3244: [Noi2013]树的计数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。