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;
}
posted @ 2019-07-30 00:07  ChenJr  阅读(404)  评论(0编辑  收藏  举报