P3565 [POI2014]HOT-Hotels(树形dp+长链剖分)
P3565 [POI2014]HOT-Hotels
參考題解
題目大意:
給定一棵樹,在樹上選 3 個(gè)點(diǎn),要求兩兩距離相等,求方案數(shù)。
三個(gè)點(diǎn)樹上兩兩距離為d存在下面兩種情況
- 某個(gè)點(diǎn)三個(gè)子樹(保證該點(diǎn)是LCA)中分別由三個(gè)點(diǎn)距離它為d
- 對(duì)于某一個(gè)點(diǎn),它的 d 級(jí)祖先以及子樹內(nèi)兩個(gè)以它為L(zhǎng)CA,距它 d 的點(diǎn)
對(duì)于情況一,設(shè)計(jì)dp: fu,jf_{u,j}fu,j?表示以uuu為根的子樹,距i距離為jjj的點(diǎn)數(shù)
對(duì)于情況二,設(shè)計(jì)dp:gu,jg_{u,j}gu,j?表示以uuu為根的子樹,兩個(gè)點(diǎn)的到LCA距離相等(此出用d表示),LCA到uuu的距離為d?jd-jd?j的點(diǎn)對(duì)
對(duì)于fu,jf_{u,j}fu,j?的狀態(tài)轉(zhuǎn)移十分顯然:fu,j=∑v∈sonufv,j?1f_{u,j}=\sum_{v\in son_u }f_{v,j-1}fu,j?=∑v∈sonu??fv,j?1?
而對(duì)于gu,jg_{u,j}gu,j?來說存在兩種情況
- 某個(gè)子樹內(nèi)部存在兩個(gè)點(diǎn):gu,j=∑v∈sonugv,j+1g_{u,j}=\sum_{v\in son_u}g_{v,j+1}gu,j?=∑v∈sonu??gv,j+1?
- 兩個(gè)不同的子樹各貢獻(xiàn)一個(gè)點(diǎn),以uuu為L(zhǎng)CA: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,w∈sonu?,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?是同一種情況,這里我們讓vvv是uuu較靠前的兒子即可避免重復(fù)計(jì)算
而對(duì)于答案計(jì)算來說也存在兩種情況:
首先對(duì)于三個(gè)點(diǎn)樹上兩兩距離為d的情況都可以看成兩個(gè)點(diǎn)在一個(gè)子樹中,而另一個(gè)點(diǎn)“別處”
- 在子樹vvv中選一個(gè)點(diǎn),而在其他子樹中選兩個(gè)點(diǎn):gu,j+1×fv,jg_{u,j+1}×f_{v,j}gu,j+1?×fv,j?
- 在子樹vvv中選兩個(gè)點(diǎn),而在其他子樹中選一個(gè)點(diǎn):fu,j?1×gv,jf_{u,j-1}×g_{v,j}fu,j?1?×gv,j?
同樣為了避免重復(fù)計(jì)算只需要讓其他子樹搞成vvv前面的子樹即可
注意上述gu,j+1g_{u,j+1}gu,j+1?和fu,j?1f_{u,j-1}fu,j?1?都還未算vvv對(duì)其的貢獻(xiàn),這個(gè)只需要先計(jì)算答案在加貢獻(xiàn)即可。
計(jì)算狀態(tài)數(shù)組和答案時(shí),都有計(jì)算前面子樹的情況,直接運(yùn)用前綴和的思想即可邊計(jì)算答案,邊轉(zhuǎn)移數(shù)組。
這里要提一點(diǎn),我們至今沒有提到gu,0g_{u,0}gu,0?對(duì)答案的貢獻(xiàn),它確實(shí)對(duì)答案有貢獻(xiàn),但是我們已經(jīng)計(jì)算過了,如果在此加上會(huì)重復(fù)計(jì)算。
而其他博主在計(jì)算的時(shí)候加上gu,0g_{u,0}gu,0?實(shí)際上增加的時(shí)gwson,1g_{wson,1}gwson,1?即重兒子的貢獻(xiàn)。
根據(jù)g的轉(zhuǎn)移方程可知道:gu,0g_{u,0}gu,0?的貢獻(xiàn)全部來自于∑v∈sonugv,1\sum_{v\in son_u}g_{v,1}∑v∈sonu??gv,1?,而計(jì)算fu,j?1×gv,jf_{u,j-1}×g_{v,j}fu,j?1?×gv,j?對(duì)答案的貢獻(xiàn)時(shí)我們具體寫一下fu,0×gv,1f_{u,0}×g_{v,1}fu,0?×gv,1?而fu,0=1f_{u,0}=1fu,0?=1,因此已經(jīng)計(jì)算過gu,0g_{u,0}gu,0?的貢獻(xiàn)!!!
然后就是長(zhǎng)鏈剖分優(yōu)化dp,每次保存長(zhǎng)兒子的貢獻(xiàn),其他兒子暴力合并,每條長(zhǎng)鏈都會(huì)在鏈頭暴力合并一次時(shí)間復(fù)雜度O(len)O(len)O(len)總的合并時(shí)間復(fù)雜度O(n)O(n)O(n)
最后如果寫指針版本的話,由于g數(shù)組是倒著轉(zhuǎn)移的,fff要多開一倍的內(nèi)存,否則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];// 加上重兒子的貢獻(xiàn)}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);// 邊計(jì)算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];}// 邊轉(zhuǎn)移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;//多開一倍的內(nèi)存g[1]=now;now+=dep[1];dfs2(1,0);cout<<ans<<'\n';return 0; }菜菜的我搞了一天這個(gè)題啊啊啊啊
總結(jié)
以上是生活随笔為你收集整理的P3565 [POI2014]HOT-Hotels(树形dp+长链剖分)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么缩小WIFi范围怎么控制无线路由器的
- 下一篇: 专为电脑而设计的照明设备专为电脑而设计的