支配树[模板]

发个模板不算漏题吧.
今天考了一发必经点… 幸好lemonoi给我们考过, 打了个支配树的板子, 但是当时当且只是因为好背所以背下来了, 关于那几个定理实际上并没有证明. 今天考完后复习了一发虽然也没有证明但是重新理解了一下, 在明白半必经点和必经点的定理后, 已经能较准确的写出板子来, 不想考试的时候憋了半天…
话说今天好像要被婊了… 毕竟学的是lemonoil的板子, 基本上没什么差别, 再说他博客里面hdu4694都放了板子. 关键是我和他考试的时候都打的暴力算必经点(没用LCA)… 如有雷同纯属巧合. 苍天在上我考试时没有跟lemonoil有半点交集.
改了新的代码风格后, 重新整理了支配树的板子.

#include<stdio.h>
#include<vector>
#include<cstring>
#define clear(a) memset(a, 0, sizeof(a))
using namespace std;
const int P = 17;
const int maxn = 1e5 + 5;
int n, m, num, q, idx, ans;
int h[maxn], fa[maxn];
int dfn[maxn], id[maxn];
int bcjf[maxn], best[maxn];
int semi[maxn], idom[maxn];
int dep[maxn], anc[maxn][P + 1];
vector<int> pre[maxn], dom[maxn];
struct edge
{
    int nxt, v;
}e[maxn * 2];
inline void add(int u ,int v, int opt)
{
    e[++num].v = v, e[num].nxt= h[u], h[u] = num;
    if(opt) pre[v].push_back(u);
}
inline int pushd(int u)
{
    if(u == bcjf[u]) return u;
    int ff = pushd(bcjf[u]);
    if(dfn[semi[best[bcjf[u]]]] < dfn[semi[best[u]]]) best[u] = best[bcjf[u]];
    return bcjf[u] = ff;
}
inline void dfs(int u)
{
    dfn[u] = ++ idx;
    id[idx] = u;
    for (int i = h[u]; i; i = e[i].nxt)
        if(!dfn[e[i].v])
            dfs(e[i].v), fa[e[i].v] = u;
}
inline void Lengauer_Tarjan()
{
    dfs(1);
    for (int i = 1; i <= n; ++ i) semi[i] = best[i] = bcjf[i] = i;
    for (int i = idx; i >= 2; -- i)
    {
        int u = id[i];
        for (int p = 0; p < pre[u].size(); ++ p)
        {
            int v = pre[u][p];
            if(!dfn[v]) continue;
            pushd(v);
            if(dfn[semi[best[v]]] < dfn[semi[u]]) semi[u] = semi[best[v]];
        }
        dom[semi[u]].push_back(u);
        bcjf[u] = fa[u];

        u = id[i - 1];
        for (int p = 0; p < dom[u].size(); ++ p)
        {
            int v = dom[u][p];
            pushd(v);
            if(semi[best[v]] == u) idom[v] = u;
            else idom[v] = best[v];
        }
    }
    for (int i = 2; i <= idx; ++i)
    {
        int u = id[i];
        if(idom[u] != semi[u]) idom[u] = idom[idom[u]];
    }
}
inline int query(int u, int v)
{
    if(dep[u] < dep[v]) swap(u, v);
    int t = dep[u] - dep[v];
    for (int i = 0; i <= P; ++ i)
        if(t & (1 << i)) u = anc[u][i];
    for (int i = P; i >= 0; --i)
        if(anc[u][i] != anc[v][i])
            u = anc[u][i], v = anc[v][i];
    return (u == v) ? u : anc[u][0];
}
inline void dfs_tree(int u)
{
    for (int i = 1; i <= P; ++ i)
    {
        if(dep[u] < (1 << i)) break;
        anc[u][i] = anc[anc[u][i - 1]][i - 1];
    }
    for (int i = h[u]; i; i = e[i].nxt)
    {
        anc[e[i].v][0] = u;
        dep[e[i].v] = dep[u] + 1;
        dfs_tree(e[i].v);
    }
}
int main()
{
    scanf("%d%d%d", &n, &m, &q);
    int u, v;
    for (int i = 1; i <= m; ++ i)
    {
        scanf("%d%d", &u, &v);
        add(u, v, 1);
    }
    Lengauer_Tarjan();
    num = 0, clear(h);
    for (int i = 2; i <= n; ++ i)
        add(idom[i], i, 0);
    dep[1] = 1, anc[1][0] = 1;
    dfs_tree(1);
    for (int i = 1; i <= q; ++ i)
    {
        int k;
        ans = 0;
        scanf("%d%d", &k, &u);
        for (int j = 1; j < k; ++ j)
            scanf("%d", &v), u = query(u, v);
        printf("%d\n", dep[u]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值