生活随笔
收集整理的這篇文章主要介紹了
【支配树模板】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
支配樹模板題
注:本文均從網絡上摘抄
首先介紹一下什么叫支配樹。
1.支配點:
在有向圖中,若刪除了點x,u到v不連通了,那么稱x支配v。
2.支配樹:
滿足樹上一個點x的所有祖先都是它的支配點的樹。
下面介紹一下一般有向圖的支配樹構建方法:
下面化還是一些名詞解釋:
1.dfs樹:
從圖的一個點S開始dfs整張圖,可以提取出一顆dfs樹,并且記x的dfs序為dfn[x]。
2.半支配點:
假設存在一個點y,從y出發有一條到x的路徑,并且路徑上任意一點z(z!=x && z!=y)都滿足dfn[z]>dfn[x]dfn[z]>dfn[x]dfn[z]>dfn[x],則稱y為x的半支配點。
記semi[x]semi[x]semi[x]為x的dfn最小的半支配點,因為x在dfs樹上的父親也是它的一個半支配點,所以semi[x]semi[x]semi[x]一定是x的祖先。并且刪掉原圖中的非樹邊后,連邊(semi[x],x),不改變原圖中的支配點關系。
求出了semi,我們就把圖變成了一個DAG,然后就可以重復DAG的做法了(不過我沒有講DAG的做法qwq),下面介紹一種更優的做法。
求半支配點:
對于一個點x,我們找到所有邊(y,x)對應的y。
若dfn[y]<dfn[x]dfn[y]<dfn[x]dfn[y]<dfn[x]且dfn[y]dfn[y]dfn[y]比當前找到的semi[x]的dfn小,則semi[x]=ysemi[x] = ysemi[x]=y
若dfn[y]>dfn[x]dfn[y]>dfn[x]dfn[y]>dfn[x],找到樹上y的一個祖先z,且dfn[z]>dfn[x]dfn[z]>dfn[x]dfn[z]>dfn[x],比較dfn[semi[z]]dfn[semi[z]]dfn[semi[z]]和dfn[semi[x]]dfn[semi[x]]dfn[semi[x]]的大小,決定是否用semi[z]semi[z]semi[z]更新semi[x]semi[x]semi[x]。
從半支配點到支配點:
對于x,我們要求它在支配樹上的父親,也就是idom[x]idom[x]idom[x]。
方法如下:
記P為從semi[x]semi[x]semi[x]到x的樹上路徑點集(不包括semi[x]),而z是P中dfn[semi[z]]dfn[semi[z]]dfn[semi[z]]最小的點。若semi[z]==semi[x]semi[z]==semi[x]semi[z]==semi[x],則有idom[x]=semi[x]idom[x]=semi[x]idom[x]=semi[x],否則有idom[x]=idom[z]idom[x] = idom[z]idom[x]=idom[z]。
算法流程:
帶權并查集實現即可。
#include <bits/stdc++.h>
using namespace std
;
const int maxn
= 2e5+7;
int n
,m
,cnt
= 0;
int p
[maxn
],val
[maxn
],sdom
[maxn
],idom
[maxn
],ans
[maxn
];
int dfn
[maxn
],id
[maxn
],fa
[maxn
];
vector
<int> nxt
[maxn
],pre
[maxn
],lat
[maxn
],zps
[maxn
];
void dfs(int now
)
{dfn
[now
] = ++cnt
;id
[cnt
] = now
;for(int i
=0;i
<nxt
[now
].size();i
++){int v
= nxt
[now
][i
];if(dfn
[v
]) continue;dfs(v
);fa
[v
] = now
;}
}
int find(int x
)
{if(x
==p
[x
]) return x
;int rt
= find(p
[x
]);if(dfn
[sdom
[val
[p
[x
]]]]<dfn
[sdom
[val
[x
]]]) val
[x
] = val
[p
[x
]];return p
[x
] = rt
;
}
void tarjan()
{for(int i
=cnt
;i
>=2;i
--){int now
= id
[i
];for(int j
=0;j
<pre
[now
].size();j
++){int v
= pre
[now
][j
];if(!dfn
[v
]) continue;find(v
);if(dfn
[sdom
[val
[v
]]]<dfn
[sdom
[now
]]) sdom
[now
] = sdom
[val
[v
]];}lat
[sdom
[now
]].push_back(now
);p
[now
] = fa
[now
];now
= fa
[now
];for(int j
=0;j
<lat
[now
].size();j
++){int v
= lat
[now
][j
];find(v
);if(sdom
[val
[v
]]==now
) idom
[v
] = now
;else idom
[v
] = val
[v
];}}for(int i
=2;i
<=cnt
;i
++){int now
= id
[i
];if(idom
[now
]!=sdom
[now
]) idom
[now
] = idom
[idom
[now
]];}
}
void gao(int now
)
{ans
[now
] = 1;for(int i
=0;i
<zps
[now
].size();i
++){int v
= zps
[now
][i
];gao(v
);ans
[now
] += ans
[v
];}
}
int main()
{scanf("%d%d",&n
,&m
);for(int i
=1;i
<=m
;i
++){int u
,v
;scanf("%d%d",&u
,&v
);nxt
[u
].push_back(v
);pre
[v
].push_back(u
);}for(int i
=1;i
<=n
;i
++){sdom
[i
] = p
[i
] = val
[i
] = i
;}dfs(1);tarjan();for(int i
=2;i
<=n
;i
++){if(idom
[i
]) zps
[idom
[i
]].push_back(i
);}gao(1);for(int i
=1;i
<=n
;i
++) printf("%d ",ans
[i
]);puts("");return 0;
}
總結
以上是生活随笔為你收集整理的【支配树模板】的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。