HDU - 6604 Blow up the city

Blow up the city

Time Limit: 5000/5000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)

Problem Description

Country A and B are at war. Country A needs to organize transport teams to deliver supplies toward some command center cities.

In order to ensure the delivery works efficiently, all the roads in country A work only one direction. Therefore, map of country A can be regarded as DAG( Directed Acyclic Graph ). Command center cities only received supplies and not send out supplies.

Intelligence agency of country B is credibly informed that there will be two cities carrying out a critical transporting task in country A.

As long as any one of the two cities can not reach a command center city, the mission fails and country B will hold an enormous advantage. Therefore, country B plans to destroy one of the n cities in country A and all the roads directly connected. (If a city carrying out the task is also a command center city, it is possible to destroy the city to make the mission fail)

Now country B has made q hypotheses about the two cities carrying out the critical task.
Calculate the number of plan that makes the mission of country A fail.

Input

The first line contains a integer T T T ( 1 ≤ T ≤ 10 1≤T≤10 1T10), denoting the number of test cases.

In each test case, the first line are two integers n , m n,m n,m, denoting the number of cities and roads( 1 ≤ n ≤ 100 , 000 , 1 ≤ m ≤ 200 , 000 1≤n≤100,000,1≤m≤200,000 1n100,000,1m200,000).
Then m lines follow, each with two integers u u u and v v v, which means there is a directed road from city u u u to v v v ( 1 ≤ u , v ≤ n , u ≠ v 1≤u,v≤n,u≠v 1u,vn,u̸=v).

The next line is a integer q, denoting the number of queries ( 1 ≤ q ≤ 100 , 000 1≤q≤100,000 1q100,000)
And then q lines follow, each with two integers a and b, which means the two cities carrying out the critical task are a and b ( 1 ≤ a , b ≤ n , a ≠ b 1≤a,b≤n,a≠b 1a,bn,a̸=b).

A city is a command center if and only if there is no road from it (its out degree is zero).

Output

For each query output a line with one integer, means the number of plan that makes the mission of country A fail.

Sample Input

2
8 8
1 2
3 4
3 5
4 6
4 7
5 7
6 8
7 8
2
1 3
6 7
3 2
3 1
3 2
2
1 2
3 1

Sample Output

4
3
2
2

Tips

题意:

两个国家 A A A B B B 打仗, B B B 打算干扰 A A A 的运输系统。已知 A A A 的运输系统是一个DAG,其中出度为 0 0 0 的结点为指挥中心(可能不止一个),有两个结点是物资的出发点,物资从出发点沿边运送到指挥中心。如果 B B B 可以炸掉一个点(任意一个,包括出发点和指挥中心,炸完后和它连的边也都炸没了),使得两个出发点中有任意一个无法到达指挥中心,则算一次成功的干扰。现在问你 q q q 次,每次假定一组 a a a b b b 为物资的出发点,问 B B B 有多少种方法实施成功的干扰。

题解:

显然,把出发点炸了一定可以;其次,如果某出发点只可以到达一个指挥中心,那么把这个指挥中心炸了也可行;除此之外, B B B 炸了能成功的点只能是从出发点到指挥中心路径上的点。而且,这个点还必须得是从出发点到指挥中心的必经点

这就由此引入了支配树的概念。支配树以终点为根节点,树上任一点到根节点路径上的所有点是以它为起点到终点的所有必经点。利用这一数据结构可以快速求解从任一结点出发向某一确定终点的路径上的必经点。

以下内容摘自维基百科 - 支配

在计算机科学中,控制流图的一个节点 d d d 支配节点 n n n,当且仅当从开始节点(可以理解为源)到节点 n n n 的每一条路径均要经过节点 d d d ,写作 d  dom  n d\text{ dom }n d dom n (一写作 d ≫ n d \gg n dn )。根据上述定义,容易得到每个节点均控制其自身。

  • 我们说一个节点 d d d 严格控制节点 n n n ,当且仅当 d d d 控制 n n n 而不等于 n n n
  • 节点 n n n最近必经点 immediate dominator \text{immediate dominator} immediate dominator),简称 idom \text{idom} idom ,是一个独特的节点,它严格支配节点 n n n ,却不支配任何严格支配节点 n n n 的其他节点。不是所有的节点均有最近必经点,如开始节点就没有。
  • 一个节点 d d d可支配边界是一个点集,其中任意节点 n n n 均满足, d d d 能严格支配所有节点 u u u ⟨ u , v ⟩ \langle u,v \rangle u,v 是图中的一条有向边),却不能严格支配 n n n 。就是 d d d 支配能力的极限。
  • 一个支配树是一棵树,它的所有节点儿子是被其最近支配的所有节点。由于最近必经点是唯一的,故其为一棵树,开始节点即为树根。
  • 求解支配树一般使用 Tarjan \text{Tarjan} Tarjan 算法,复杂度 O ( V + E ) O(V+E) O(V+E)

由于本题可能有不止一个终点,因此我们需要先找到所有指挥中心,然后把它们都连向一个新的结点,求解以这个新结点为根节点的支配树。先假设它已经建好,那么,对于任一询问的两个结点 a a a b b b ,最后的答案有三部分:第一,它们自身两个;第二,它们唯一可达的指挥中心;第三,从它们到指挥中心的路径上的必经点。而进一步分析,支配树上自己是支配自己的,所以第一部分被囊括进来;第二部分也是在支配树上的,因为只有指挥中心会连向根节点,那么,与它们唯一可达的指挥中心一定是其到达根节点的必经点;而第三部分显然是在支配树上的。因此,最后的答案即为 a a a 的必经点集合并上 b b b 的必经点集合的总集合的大小,也就是 a a a 的必经点个数加上 b b b 的必经点个数减去它们共有的必经点个数,而这个个数就是 a a a b b b LCA \text{LCA} LCA 的必经点个数。所以答案为 a a a 的深度加 b b b 的深度减 LCA ( a , b ) \text{LCA}(a,b) LCA(a,b) 的深度。

现在就只剩下建立支配树了。对于本题DAG的图结构,我们有一个较一般图简单一些的求解方法。首先,我们知道,一个结点的必经点(特指到那个新结点的必经点,下同)在逆拓扑排序中一定是在它前面的,所以要从终点向前考虑问题。我们先求出原图的逆拓扑排序,然后从这个序列中依次拿出结点进行建图。每拿出一个结点 v v v ,我们就考虑所有由它直接相连的结点集合 V V V 。一个结点 u u u 能成为 v v v 的必经点,那它必须是 V V V 中所有点共同的必经点(否则就称不上“必经”),也就是支配树上这些结点的 LCA \text{LCA} LCA 。又由于 V V V 中的点和 v v v 是直接相连的,因此 u u u 一定是 v v v 的最近必经点,即,在支配树上, u u u v v v 的父亲。像这样按顺序处理完所有结点,一棵支配树就建立好了。

#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
const int MAXM=4e5+10;
const int DEG=20;
struct Edge{
    int to,next;
}edge[MAXM];
int tot,head[MAXN];
inline void init(){
    tot=0;
    memset(head,-1,sizeof(head));
}
int deg[MAXN];
inline void add_edge(int u,int v){
    edge[tot]=(Edge){v,head[u]};
    head[u]=tot++;
    edge[tot]=(Edge){u,head[v]};
    head[v]=tot++;
    ++deg[u];
}
queue<int> topo;
void bfs(int n){
    while (!topo.empty()) topo.pop();
    queue<int> Q;
    for (int u=1;u<=n;++u){
        if (!deg[u]){
            Q.push(u);
            add_edge(u,0);
        }
    }
    while (!Q.empty()){
        int u=Q.front();
        Q.pop();
        topo.push(u);
        for (int i=head[u];~i;i=edge[i].next){
            if (!(i&1)) continue;
            if (!(--deg[edge[i].to]))
                Q.push(edge[i].to);
        }
    }
}
int fa[MAXN][DEG]={0},dep[MAXN]={0};
int LCA(int u, int v){
    if (dep[u]>dep[v])
        swap(u,v);
    int hu=dep[u],hv=dep[v];
    int tu=u,tv=v;
    for (int det=hv-hu,i=0;det;det>>=1,++i)
        if (det&1) tv=fa[tv][i];
    if (tu==tv) return tu;
    for (int i=DEG-1;i>=0;--i){
        if (fa[tu][i]==fa[tv][i])
            continue;
        tu=fa[tu][i];
        tv=fa[tv][i];
    }
    return fa[tu][0];
}
void bfs(){
    while (!topo.empty()){
        int u=topo.front();
        topo.pop();
        int v=-1;
        for (int i=head[u];~i;i=edge[i].next){
            if (i&1) continue;
            if (v==-1) v=edge[i].to;
            else v=LCA(edge[i].to,v);
        }
        dep[u]=dep[v]+1;
        fa[u][0]=v;
        for (int i=1;i<DEG;++i)
            fa[u][i]=fa[fa[u][i-1]][i-1];
    }
}
int T,n,m,q;
int u,v;
int main(){
    scanf("%d",&T);
    while (T--){
        scanf("%d%d",&n,&m);
        init();
        memset(deg,0,sizeof(deg));
        while (m--){
            scanf("%d%d",&u,&v);
            add_edge(u,v);
        }
        bfs(n);
        bfs();
        scanf("%d",&q);
        while (q--){
            scanf("%d%d",&u,&v);
            printf("%d\n",dep[u]+dep[v]-dep[LCA(u,v)]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值