首页 > 其他分享 >【题解】P4899 [IOI2018] werewolf 狼人

【题解】P4899 [IOI2018] werewolf 狼人

时间:2023-01-12 18:45:01浏览次数:50  
标签:rs int 题解 kruskal 狼人 dep P4899 ls maxm

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

题意

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

  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;
}

标签:rs,int,题解,kruskal,狼人,dep,P4899,ls,maxm
From: https://www.cnblogs.com/lingspace/p/p4899.html

相关文章

  • 传递游戏【题解】
    Description毛大神最近在玩一个传递游戏,即有\(N\)个人在做传递物品的游戏,这N个人的编号为\(1,2,3,...,N-1,N\)。游戏规则是这样的:开始时物品可以在任意一人手上,他可把物......
  • 表达式的值【题解】
    [NOIP2011普及组]表达式的值题目描述对于1位二进制变量定义两种运算:运算的优先级是:先计算括号内的,再计算括号外的。“×”运算优先于“⊕”运算,即计算表达式......
  • [NOIP2017 普及组]跳房子 【题解】
    题目背景NOIP2017普及组T4题目描述跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。跳房子的游戏规则如下:在地面上确定一个起点,然后在起点......
  • 洛谷 P8077 [WC2022] 序列变换 题解
    题目链接。WC2023之前补一下WC2022的题,参考了官方题解。首先,把括号序列转化为二叉树,\(\texttt{(A)B}\)转为一个点的左子树是\(A\),右子树是\(B\)。相当于括号序列先......
  • 1.8模拟赛题解
    T1考虑每次反弹后,球的运动轨迹都会偏移\(2\beta\),总偏移量即为\(2k\beta\),而最后需要回到原点,因此\(360|2k\beta\),简单求\(\gcd\)即可。T2设\(ans_k\)表示出现过......
  • 1.11模拟赛题解
    T1对于方阵\(A\),考虑其反方阵\(A'\)。容易发现\(A\)与\(A'\)的权值和相同,而其中必有一个与\(B\)的差不超过\(\lfloor\frac{nm}{2}\rfloor\),因此判断一下哪个满足......
  • 1.9模拟赛题解
    T1从左到右扫描,首先如果\(a_i<b_i\)那么一定无解,否则不断在其右边找最近的\(j\)使得\(a_j\in[b_i,a_i]\),把\(a_i\)和\(a_j\)交换。感性理解这是对的。T2先证操......
  • 1.12模拟赛题解
    T1容易知道答案为原图的最大子二分图大小。枚举每个点在二分图的左边还是右边,计算出答案。时间复杂度\(O(2^n\timesm)\)。T2考虑递推构造方案。假设现在已经有了一组......
  • POI Excel格式报表生成 同步下载问题解决
    前言解决POI导出功能,过时方法和新增样式放在最下面或者参考下文POI样式调节0.maven(新版本)<poi.version>4.1.2</poi.version> <dependency> <groupId>org.ap......
  • P6751 [IOI2019]视觉程序 题解--zhengjun
    提供一种简介易懂的做法。首先曼哈顿距离的绝对值比较难处理,所以可以转成切比雪夫距离。具体地说,就是\((x,y)\)变成\((x+y,x-y)\)(接下来所述的坐标都是变换后的)。这......