hdu 6604(支配树)
题意:
给出一个\(DAG\),每次询问两个点,求有多少方案通过删除一个点,使得其中一个点无法到达所有他所在的联通块出度为\(0\)的点。
分析:
考虑将图反向建边。因为可能有完全独立的两个\(DAG\),因此我们考虑用一个\(0\)号点作为超级源点,将若干个\(DAG\)连成一块。至此,题目就转化成在这样的图中,某两个点\(u\)和\(v\),有多少种删除一个点的方案,使得超级源点不能到达它们其中一个点。我们考虑从超级源点到\(u\)、\(v\)的过程,我们发现,在超级源点到\(u\)或\(v\)的路径上必定会有一些必须要经过的点。只要我们把这些路径上的必经点删掉,那么必定会贡献答案。故此时,我们只需要求出超级源点到点\(u\)、\(v\)的必经点的个数即可。
对于这个问题,我们可以用支配树进行解决。根据支配树的性质,在支配树上,任意两点\(u\)、\(v\)之间的必经点的个数为它们之间链的长度。因此我们只需要将支配树建出来,最后统计一下超级源点到\(u\)、\(v\)两点到深度\(depth\),最后再去除一下这两个点的公共祖先\(lca(a.b)\)的贡献即可。
在这个题中,鉴于DAG的特殊性,因此我们可以对之前反向的图进行拓扑排序,在拓扑排序的过程中,如果发现某个节点\(u\)有多个父亲结点,那么我们只需要取这多个父亲节点中所有结点的\(lca\)即为支配树上\(u\)的父亲。
整体看来,在建树过程以及询问过程均只涉及\(lca\)查询,故整体时间复杂度为\(\mathcal{O}(nlogn+qlogn)\)
代码:
#include <bits/stdc++.h>
#define maxn 200005
#define LOG 20
using namespace std;
struct Node{
int to,next;
}q1[maxn],q2[maxn],q3[maxn];
int head1[maxn],head2[maxn],head3[maxn],cnt1,cnt2,cnt3;
int dep[maxn],anc[maxn][LOG];
int inde[maxn];
vector<int>edge;
void add_edge(int from,int to,int *head,Node *q,int &cnt){
q[cnt].to=to;
q[cnt].next=head[from];
head[from]=cnt++;
}
void init(int n,int *head,int &cnt){
for(int i=0;i<=n;i++) head[i]=-1,dep[i]=0,inde[i]=0;
cnt=0;
edge.clear();
}
void swim(int &x,int h){
for(int i=0;h>0;i++){
if(h&1)
x=anc[x][i];
h>>=1;
}
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
swim(x,dep[x]-dep[y]);
if(x==y) return x;
for(int i=LOG-1;i>=0;i--){
if(anc[x][i]!=anc[y][i]){
x=anc[x][i];
y=anc[y][i];
}
}
return anc[x][0];
}
void bfs(int n){
queue<int>que;
for(int i=1;i<=n;i++){
if(!inde[i]){
que.push(i);
add_edge(0,i,head2,q2,cnt2);
add_edge(i,0,head1,q1,cnt1);
edge.push_back(i);
}
}
while(!que.empty()){
int x=que.front();
que.pop();
for(int i=head2[x];i!=-1;i=q2[i].next){
int to=q2[i].to;
inde[to]--;
if(inde[to]==0) que.push(to),edge.push_back(to);
}
}
}
void BuildTree(){
for(auto x:edge){
int fa=-1;
for(int i=head1[x];i!=-1;i=q1[i].next){
int to=q1[i].to;
if(fa==-1) fa=to;
else fa=lca(fa,to);
} fa= fa==-1?0:fa;
dep[x]=dep[fa]+1;
anc[x][0]=fa;
for(int i=1;i<=LOG-1;i++)
anc[x][i]=anc[anc[x][i-1]][i-1];
add_edge(fa,x,head3,q3,cnt3);
}
}
int main()
{
//freopen("in","r",stdin);
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
init(n,head1,cnt1);
init(n,head2,cnt2);
init(n,head3,cnt3);
for(int i=1;i<=m;i++){
int from,to;
scanf("%d%d",&from,&to);
add_edge(from,to,head1,q1,cnt1);
add_edge(to,from,head2,q2,cnt2);
inde[from]++;
}
bfs(n);
BuildTree();
int q;
scanf("%d",&q);
while(q--){
int from,to;
scanf("%d%d",&from,&to);
printf("%d\n",dep[from]+dep[to]-dep[lca(from,to)]);
}
}
return 0;
}