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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

P3565 [POI2014]HOT-Hotels(树形dp+长链剖分)

發布時間:2023/12/3 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 P3565 [POI2014]HOT-Hotels(树形dp+长链剖分) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

P3565 [POI2014]HOT-Hotels

參考題解

題目大意:
給定一棵樹,在樹上選 3 個點,要求兩兩距離相等,求方案數。

三個點樹上兩兩距離為d存在下面兩種情況

  • 某個點三個子樹(保證該點是LCA)中分別由三個點距離它為d
  • 對于某一個點,它的 d 級祖先以及子樹內兩個以它為LCA,距它 d 的點

對于情況一,設計dp: fu,jf_{u,j}fu,j?表示以uuu為根的子樹,距i距離為jjj的點數

對于情況二,設計dp:gu,jg_{u,j}gu,j?表示以uuu為根的子樹,兩個點的到LCA距離相等(此出用d表示),LCA到uuu的距離為d?jd-jd?j點對

對于fu,jf_{u,j}fu,j?的狀態轉移十分顯然:fu,j=∑v∈sonufv,j?1f_{u,j}=\sum_{v\in son_u }f_{v,j-1}fu,j?=vsonu??fv,j?1?

而對于gu,jg_{u,j}gu,j?來說存在兩種情況

  • 某個子樹內部存在兩個點:gu,j=∑v∈sonugv,j+1g_{u,j}=\sum_{v\in son_u}g_{v,j+1}gu,j?=vsonu??gv,j+1?
  • 兩個不同的子樹各貢獻一個點,以uuu為LCA:gu,j=∑v,w∈sonu,v≠wfv,j?1×fw,j?1g_{u,j}=\sum_{v,w\in son_u,v \ne w} f_{v,j-1}×f_{w,j-1}gu,j?=v,wsonu?,v?=w?fv,j?1?×fw,j?1?

很明顯,第二中情況fv,j?1×fw,j?1,fw,j?1×fv,j?1f_{v,j-1}×f_{w,j-1}, f_{w,j-1}×f_{v,j-1}fv,j?1?×fw,j?1?,fw,j?1?×fv,j?1?是同一種情況,這里我們讓vvvuuu較靠前的兒子即可避免重復計算

而對于答案計算來說也存在兩種情況:
首先對于三個點樹上兩兩距離為d的情況都可以看成兩個點在一個子樹中,而另一個點“別處

  • 在子樹vvv中選一個點,而在其他子樹中選兩個點:gu,j+1×fv,jg_{u,j+1}×f_{v,j}gu,j+1?×fv,j?
  • 在子樹vvv中選兩個點,而在其他子樹中選一個點:fu,j?1×gv,jf_{u,j-1}×g_{v,j}fu,j?1?×gv,j?

同樣為了避免重復計算只需要讓其他子樹搞成vvv前面的子樹即可
注意上述gu,j+1g_{u,j+1}gu,j+1?fu,j?1f_{u,j-1}fu,j?1?都還未算vvv對其的貢獻,這個只需要先計算答案在加貢獻即可。

計算狀態數組和答案時,都有計算前面子樹的情況,直接運用前綴和的思想即可邊計算答案,邊轉移數組。

這里要提一點,我們至今沒有提到gu,0g_{u,0}gu,0?對答案的貢獻,它確實對答案有貢獻,但是我們已經計算過了,如果在此加上會重復計算。

而其他博主在計算的時候加上gu,0g_{u,0}gu,0?實際上增加的時gwson,1g_{wson,1}gwson,1?即重兒子的貢獻。

根據g的轉移方程可知道:gu,0g_{u,0}gu,0?的貢獻全部來自于∑v∈sonugv,1\sum_{v\in son_u}g_{v,1}vsonu??gv,1?,而計算fu,j?1×gv,jf_{u,j-1}×g_{v,j}fu,j?1?×gv,j?對答案的貢獻時我們具體寫一下fu,0×gv,1f_{u,0}×g_{v,1}fu,0?×gv,1?fu,0=1f_{u,0}=1fu,0?=1,因此已經計算過gu,0g_{u,0}gu,0?的貢獻!!!


然后就是長鏈剖分優化dp,每次保存長兒子的貢獻,其他兒子暴力合并,每條長鏈都會在鏈頭暴力合并一次時間復雜度O(len)O(len)O(len)總的合并時間復雜度O(n)O(n)O(n)

最后如果寫指針版本的話,由于g數組是倒著轉移的,fff要多開一倍的內存,否則g可能倒回來覆蓋掉fff

code

#define IO ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr) #include<cstring> #include<iostream> #include<algorithm> using namespace std; using ll=long long; constexpr int N=5010; int h[N],e[2*N],ne[2*N],idx; void add(int a,int b){e[idx]=b,ne[idx]=h[a],h[a]=idx++;} int n; int dep[N],son[N]; void dfs1(int u,int fa) {for(int i=h[u];i!=-1;i=ne[i]){int v=e[i];if(v==fa) continue;dfs1(v,u);if(dep[v]>dep[son[u]]) son[u]=v;}dep[u]=dep[son[u]]+1; }ll pool[4*N]; ll *f[N],*g[N],*now=pool; ll ans; void dfs2(int u,int fa) {f[u][0]=1;if(son[u]){f[son[u]]=f[u]+1;g[son[u]]=g[u]-1;dfs2(son[u],u);ans+=g[son[u]][1];// 加上重兒子的貢獻}for(int i=h[u];i!=-1;i=ne[i]){int v=e[i];if(v==fa||v==son[u]) continue;f[v]=now;now+=dep[v]<<1;g[v]=now;now+=dep[v];dfs2(v,u);// 邊計算for(int j=0;j<dep[v];j++){if(j) ans+=f[u][j-1]*g[v][j];ans+=g[u][j+1]*f[v][j];}// 邊轉移for(int j=0;j<dep[v];j++){g[u][j+1]+=f[u][j+1]*f[v][j];if(j) g[u][j-1]+=g[v][j];f[u][j+1]+=f[v][j];}}} int main() {IO;cin>>n;memset(h,-1,sizeof h);for(int i=1;i<n;i++){int u,v;cin>>u>>v;add(u,v);add(v,u);}dfs1(1,0);f[1]=now;now+=dep[1]<<1;//多開一倍的內存g[1]=now;now+=dep[1];dfs2(1,0);cout<<ans<<'\n';return 0; }

菜菜的我搞了一天這個題啊啊啊啊

總結

以上是生活随笔為你收集整理的P3565 [POI2014]HOT-Hotels(树形dp+长链剖分)的全部內容,希望文章能夠幫你解決所遇到的問題。

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