2019杭电多校第三场B - Blow up the city HDU - 6604(支配树)

参考
题意:一个有向无环图,其中入度为0的点称为物资提供点,现在有q次操作,每次给出两个点,如果破坏图上的某个点可以使得这两个点中的任何一个无法到达物资提供点则说明破坏成功,问有多少个可以破坏成功的点。

如果我们建一张反图,是不是可以看做从物资点出发到达给定的两个点必须经过的点有哪些。
于是建反图的话==求解有向无环图的必经点问题。
而支配树或者其他的求解必经点的算法都是选定一个点出发走遍所有的图,那我们肯定不能对每个物资点都走一遍,那么可以将这些点都连到一个图以外的点上,从这个点出发走遍全图即可,这种建虚点的思想挺重要的。
我不会支配树…
不过对于一个有向无环图来说,可以通过拓扑排序加lca的方法求解,一个点到起点的必经点就是他的路径深度,如果拓扑序下一个点有多个父亲,那么这些父亲的lca就是该点在支配树上的父亲。

#include<bits/stdc++.h>
using namespace std;
#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+7;
const int maxm=4e5+7;

struct Edge{
    int v,next;
}edge[maxm<<1];

int head[maxn],headfan[maxn],top;

int fa[maxn][19];
int xxx;
int dep[maxn];
int mi=18;

int du[maxn];
void init(int n){
    memset(head,-1,sizeof(head));
    memset(headfan,-1,sizeof(headfan));
    top=0;
    dep[n+1]=1;
}

void add(int u,int v,int head[]){
    edge[top].v=v;
    edge[top].next=head[u];
    head[u]=top++;
}
queue<int> q;
int num;
int t[maxn];
int n;
void topsort(int st){
    num=0;
    while(!q.empty()) q.pop();
    //q.push(st);
    for(int i=1;i<=n;++i)
        if(du[i]==0)
            add(st,i,head),add(i,st,headfan),q.push(i);
    int u,v;
    while(!q.empty()){
        u=q.front(); q.pop();
        t[++num]=u;
        for(int i=head[u];i!=-1;i=edge[i].next){
            v=edge[i].v;
            --du[v];
            if(du[v]==0) q.push(v);
        }
    }
}

int LCA(int x,int y){
    if(x==-1||y==-1) return -1;
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=mi;i>=0;--i)
        if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    if(x==y) return x;
    for(int i=mi;i>=0;--i)
        if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

int main(){
    int case1;
    int m,u,v;
    scanf("%d",&case1);
    while(case1--){
        scanf("%d%d",&n,&m);
        init(n);
        while(m--){
            scanf("%d%d",&u,&v);
            add(v,u,head),add(u,v,headfan);
            ++du[u];
        }
        topsort(n+1);
        int lca;
        for(int i=1;i<=num;++i){
            u=t[i];
            lca=-1;
            if(headfan[u]!=-1) lca=edge[headfan[u]].v;
            for(int j=headfan[u];j!=-1;j=edge[j].next){
                v=edge[j].v;
                lca=LCA(lca,v);
            }
            dep[u]=dep[lca]+1;
            fa[u][0]=lca;
            //cout<<lca<<endl;
            for(int j=1;j<=mi;++j) fa[u][j]=fa[fa[u][j-1]][j-1];
        }
        int q;
        scanf("%d",&q);
        while(q--){
            scanf("%d%d",&u,&v);
            printf("%d\n",dep[u]+dep[v]-dep[LCA(u,v)]-1);
        }
        for(int i=1;i<=n;++i) dep[i]=0,du[i]=0;
        dep[n+1]=0;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值