HDU 6604 Blow up the city DAG图上建支配树

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6604

DAG图上的支配树

先来说一下支配树的简单概念,比如说我们要到a节点必须经过b节点,那么b节点就是a的支配点,在一棵支配树下,所有节点都是子树节点的支配点。

总所周知DAG图是有向无环图,因此处理起来会比一般图上的轻松很多。建树时我们通过拓扑序可以轻松遍历出所有点,然后我们逆着拓扑序看,如果这个点的所有出度的父节点都已经确定了,那么他的父节点就是他所有出度的点的公共祖先节点(不理解的可以动手画图),因为是逆拓扑序,因此这个点的所有出度肯定已经处理完毕了,可以保证做法的正确性。

题意

然后我们来看题意,给一个DAG图,然后询问每次给两个点,你可以堵掉一个点使得任意两个点中有一个无法到达出度为0的点,问有多少点可以堵。

分析

转化一下题意就变成了支配树上两个点之间经历了多少个点,这就是简单的度数问题了,我们求一个 d e p [ x ] + d e p [ y ] − d e p [ l c a ( x , y ) ] dep[x]+dep[y]-dep[lca(x,y)] dep[x]+dep[y]dep[lca(x,y)]就可以了。

Code

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define re register
typedef long long ll;
typedef pair<ll, ll> PII;
typedef unsigned long long ull;
const int N = 1e5 + 10, M = 1e6 + 5, INF = 0x3f3f3f3f;
const int MOD = 1e9+7;
vector<int> G1[N], G2[N];
int in[N], n, m, q[N], idx;
int f[N][25], dep[N];
void tupo() {
    idx = 0; queue<int> que;
    for (int i = 1; i <= n; i++) {
        if (!in[i]) que.push(i);
        if (!G1[i].size()) G1[i].push_back(0), in[0]++;
    }
    while (que.size()) {
        int now = que.front();
        que.pop();
        q[++idx] = now;
        for (auto v : G1[now]) {
            in[v]--;
            if (!in[v]) que.push(v);
        }
    }
}
int lca(int x, int y) {
    if (dep[x] > dep[y]) swap(x, y);
    for (int i = 20; i >= 0; i--) if (dep[f[y][i]] >= dep[x]) y = f[y][i];

    if (x == y) return x;
    for (int i = 20; i >= 0; i--) {
        if (f[y][i] != f[x][i])
            y = f[y][i], x = f[x][i];
    }
    return f[x][0];
}

void solve() {
    int T; cin >> T; while (T--) {
        cin >> n >> m;
        for (int i = 0; i <= n; i++) G1[i].clear(), G2[i].clear(), dep[i] = 0;
        for (int i = 1; i <= m; i++) {
            int u, v; cin >> u >> v;
            G1[u].push_back(v);
            in[v]++;
        }
        tupo();
        for (int i = n; i >= 1; i--) {
            int now = q[i];
            if (!G1[now].size()) continue;
            int _lca = G1[now][0];
            for (int j = 1; j < G1[now].size(); j++) _lca = lca(_lca, G1[now][j]);
            f[now][0] = _lca;
            dep[now] = dep[_lca] + 1;
            for (int j = 1; j <= 20; j++) f[now][j] = f[f[now][j-1]][j-1];
        }
        int qry; cin >> qry; while (qry--) {
            int x, y; cin >> x >> y;
            cout << dep[x] + dep[y] - dep[lca(x, y)] << endl;
        }
    }
}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
#endif
    solve();
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值