Loading

【题解】P4899 [IOI2018] werewolf 狼人

そうやってただ日が暮れるまで語り掛ける本当の言葉

题意

给定一个有向图和若干询问,每次询问是否存在一条满足条件的路径:

  1. 端点分别为 \(u, v\)

  2. 前面一段不经过 \([1, L]\) 的结点,后面一段不经过 \([R, N]\) 的结点

思路

kruskal 重构树。

首先有条件判定连通性考虑 kruskal 重构树。

这里前面和后面的问题是镜像的,分别维护两棵 kruskal 重构树即可。

kruskal 重构树的性质:从结点 \(u\) 出发,经过边权不超过 \(w\) 的边,可以到达的结点 <-> kruskal 重构树上从 \(u\) 出发,经过边权不超过 \(w\) 的边,可以到达的深度最小祖先的子树。

所以这里考虑将 \(u, v\) 在 kruskal 重构树上跳祖先,然后分别判断两棵重构树内 \(u, v\) 的子树是否有交集。

这里有一个人类智慧的想法:先对第二棵树搜一遍,然后将 \(dfs\) 序附在第一棵树的对应点上,跑一次线段树合并,查询只需在线段树上询问子树对应的区间就行。

时间复杂度懒得算,反正飞快。

代码

#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn = 2e5 + 5;
const int maxm = 4e5 + 5;
const int t_sz = maxm * 40;
const int lg_sz = 17;

struct Edge
{
    int u, v, w;
    
    bool operator < (const Edge& rhs) const { return (w < rhs.w); }
} edge[maxm];

int n, m, q;
int cnt, tot, cur;
int lg[maxm], dfn[maxm], out[maxm], rt[maxm], fa[maxm];
int ls[t_sz], rs[t_sz];

inline int read()
{
    int res = 0;
    char ch = getchar();
    while ((ch < '0') || (ch > '9')) ch = getchar();
    while ((ch >= '0') && (ch <= '9'))
    {
        res = res * 10 + ch - '0';
        ch = getchar();
    }
    return res;
}

void update(int &k, int l, int r, int p)
{
    k = ++cnt;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (p <= mid) update(ls[k], l, mid, p);
    else update(rs[k], mid + 1, r, p);
}

bool query(int k, int l, int r, int ql, int qr)
{
    if (!k) return false;
    if ((l >= ql) && (r <= qr)) return true;
    int mid = (l + r) >> 1;
    bool res = false;
    if (ql <= mid) res |= query(ls[k], l, mid, ql, qr);
    if (qr > mid) res |= query(rs[k], mid + 1, r, ql, qr);
    return res;
}

int merge(int u, int v)
{
    if ((!u) || (!v)) return u | v;
    int p = ++cnt;
    ls[p] = merge(ls[u], ls[v]);
    rs[p] = merge(rs[u], rs[v]);
    return p;
}

struct re_tree
{
    int c[maxm], ls[maxm], rs[maxm], dep[maxm], f[maxm][lg_sz];

    void get_dep(int u)
    {
        if (ls[u])
        {
            f[ls[u]][0] = f[rs[u]][0] = u;
            dep[ls[u]] = dep[rs[u]] = dep[u] + 1;
            get_dep(ls[u]), get_dep(rs[u]);
        }
    }

    void dfs1(int u)
    {
        dfn[u] = ++cur;
        if (ls[u]) dfs1(ls[u]), dfs1(rs[u]);
        out[u] = cur;
    }

    void dfs2(int u)
    {
        if (ls[u])
        {
            dfs2(ls[u]), dfs2(rs[u]);
            rt[u] = merge(rt[ls[u]], rt[rs[u]]);
        }
        else update(rt[u], 1, tot, dfn[u]);
    }

    void init()
    {
        get_dep(tot);
        c[0] = tot + 1;
        for (int j = 1; j < 16; j++)
            for (int i = 1; i <= tot; i++)
                f[i][j] = f[f[i][j - 1]][j - 1];
    }

    int get_anc(int u, int w)
    {
        int k = lg[dep[u]];
        while (k--)
            while (c[f[u][k]] <= w)
                u = f[u][k];
        return u;
    }
} t1, t2;

int get(int x) { return (fa[x] == x ? x : fa[x] = get(fa[x])); }

void build(re_tree &t)
{
    sort(edge + 1, edge + m + 1);
    tot = n;
    for (int i = 1; i <= 2 * n; i++) fa[i] = i;
    for (int i = 1, fu, fv; i <= m; i++)
    {
        fu = get(edge[i].u), fv = get(edge[i].v);
        if (fu != fv)
        {
            fa[fu] = fa[fv] = ++tot;
            t.c[tot] = edge[i].w;
            t.ls[tot] = fu, t.rs[tot] = fv;
        }
    }
}

int main()
{
    n = read(), m = read(), q = read();
    for (int i = 1; i <= m; i++) edge[i].u = read() + 1, edge[i].v = read() + 1;
    for (int i = 1; i <= m; i++) edge[i].w = max(edge[i].u, edge[i].v);
    build(t1);
    for (int i = 1; i <= m; i++) edge[i].w = -min(edge[i].u, edge[i].v);
    build(t2);
    for (int i = 1; i < (1 << 16); i++) lg[i] = lg[i >> 1] + 1;
    for (int i = (1 << 16); i <= tot; i++) lg[i] = 16;
    t1.init(), t2.init();
    t2.dfs1(tot), t1.dfs2(tot);
    for (int i = 1, u, v, l, r; i <= q; i++)
    {
        u = read() + 1, v = read() + 1, l = read() + 1, r = read() + 1;
        u = t2.get_anc(u, -l), v = t1.get_anc(v, r);
        printf("%d\n", query(rt[v], 1, tot, dfn[u], out[u]));
    }
    return 0;
}
posted @ 2023-01-12 18:37  kymru  阅读(25)  评论(0编辑  收藏  举报