[IOI2018] werewolf 狼人 kruskal重构树,主席树

[IOI2018] werewolf 狼人

LG传送门

kruskal重构树好题。

日常安利博客文章

这题需要搞两棵重构树出来,这两棵重构树和我们平时见过的重构树有点不同(据说叫做点权重构树?),根据经过我们简化的建树方法,这两棵树不再是二叉树,但是仍具有kruskal重构树的优秀性质,建议结合后面的描述理解。

看这题需要首先我们从\(S\)走到\(T\)转化为分别从\(S\)和\(T\)出发寻找能共同到达的点,需要快速求出从某个点出发经过点权不大(小)于\(r\)(\(l\))的点,考虑kruskal重构树。令每条边的的边权为所连接两点的较大(小)值,造两棵重构树,这样就可以像平时一样直接倍增做了,但是我们发现边权的信息实际上就是点权的信息,于是我们在建新树时就不另建新点了,这就是所谓的“点权重构树”。建出树处理倍增之后,我们的问题就变成了查询两棵树上两棵子树是否有交,用dfs序表达就是一个简单的二维数点的问题,直接主席树。

#include <cstdio>
#include <cctype>
#include <vector>
#define R register
#define I inline
#define B 1000000
using namespace std;
const int N = 200003;
char buf[B], *p1, *p2;
I char gc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, B, stdin), p1==p2) ? EOF : *p1++; }
I int rd() {
    R int f = 0;
    R char c = gc();
    while (c < 48 || c > 57) c = gc();
    while (c > 47 && c < 58) f = f * 10 + (c ^ 48), c = gc();
    return f;
}
int s[N], rt[N], val[N], T;
vector <int> g[N];
struct edge { int g, s; };
struct segtree { int p, q, s; }e[N << 5];
struct kruskal {
    int h[N], f[N], fa[N][20], dfn[N], low[N], E, tim;
    edge e[N];
    I void add(int x, int y) { e[++E] = (edge){y, h[x]}, h[x] = E; }
    I int find(int x) {
        R int r = x, y;
        while (f[r] ^ r)
            r = f[r];
        while (x ^ r)
            y = f[x], f[x] = r, x = y;
        return r;
    }
    void dfs(int x) {
        dfn[x] = ++tim;
        R int i;
        for (i = 1; i < 20; ++i)
            fa[x][i] = fa[fa[x][i - 1]][i - 1];
        for (i = h[x]; i; i = e[i].s)
            dfs(e[i].g);
        low[x] = tim;
    }
}X, Y;
int modify(int k, int l, int r, int x) {
    R int t = ++T;
    e[t].p = e[k].p, e[t].q = e[k].q, e[t].s = e[k].s + 1;
    if (l == r)
        return t;
    R int m = l + r >> 1;
    if (x <= m)
        e[t].p = modify(e[k].p, l, m, x);
    else
        e[t].q = modify(e[k].q, m + 1, r, x);
    return t;
}
int query(int k, int t, int l, int r, int x, int y) {
    if (x <= l && r <= y)
        return e[t].s - e[k].s;
    R int m = l + r >> 1, o = 0;
    if (x <= m)
        o += query(e[k].p, e[t].p, l, m, x, y);
    if (m < y)
        o += query(e[k].q, e[t].q, m + 1, r, x, y);
    return o;
}
int main() {
    R int n = rd(), m = rd(), Q = rd(), i, x, y, l, r;
    for (i = 1; i <= m; ++i)
        x = rd() + 1, y = rd() + 1, g[x].push_back(y), g[y].push_back(x);
    for (i = 1; i <= n; ++i)
        X.f[i] = i, Y.f[i] = i, s[i] = g[i].size();
    for (x = n; x; --x)
        for (i = 0; i < s[x]; ++i)
            if (g[x][i] > x && (y = X.find(g[x][i])) ^ x)
                X.add(x, y), X.f[y] = X.fa[y][0] = x;
    for (x = 1; x <= n; ++x)
        for (i = 0; i < s[x]; ++i)
            if (g[x][i] < x && (y = Y.find(g[x][i])) ^ x)
                Y.add(x, y), Y.f[y] = Y.fa[y][0] = x;
    X.dfs(1), Y.dfs(n);
    for (i = 1; i <= n; ++i)
        val[X.dfn[i]] = Y.dfn[i];
    for (i = 1; i <= n; ++i)
        rt[i] = modify(rt[i - 1], 1, n, val[i]);
    while (Q--) {
        x = rd() + 1, y = rd() + 1, l = rd() + 1, r = rd() + 1;
        for (i = 19; ~i; --i)
            if (X.fa[x][i] >= l)
                x = X.fa[x][i];
        for (i = 19; ~i; --i)
            if (Y.fa[y][i] && Y.fa[y][i] <= r)
                y = Y.fa[y][i];
        printf(query(rt[X.dfn[x] - 1], rt[X.low[x]], 1, n, Y.dfn[y], Y.low[y]) ? "1\n" : "0\n");
    }
    return 0;
}

原文地址:https://www.cnblogs.com/cj-chd/p/10349360.html

时间: 2024-11-06 07:39:56

[IOI2018] werewolf 狼人 kruskal重构树,主席树的相关文章

[IOI2018]werewolf狼人——kruskal重构树+可持久化线段树

题目链接: IOI2018werewolf 题目中编号都是从0开始,太不舒服了,我们按编号从1开始讲QAQ. 题目大意就是询问每次从一个点开始走只能走编号在[l,n]中的点,在任意点变成狼,之后只能走[0,r]中的点,是否能到达另一个点. 后一部分其实就是找有哪些点只走[0,r]中的点能到达终点,那么反过来看,就是终点只走[0,r]中的点能到达哪些点. 那么只要起点能到达的点和终点能到达的点中有交集就有解. 因为起点只能走一些编号较大的点,那么我们求出原图的最大生成树,建出kruskal重构树,

P4197 Peaks [克鲁斯卡尔重构树 + 主席树]

部分kruskal重构树内容摘抄于bzt神仙的blog Problem 在\(Bytemountains\)有\(n\)座山峰,每座山峰有他的高度\(h_i\) .有些山峰之间有双向道路相连,共\(M\)条路径,每条路径有一个困难值,这个值越大表示越难走,现在有\(Q\)组询问,每组询问询问从点\(v\)开始只经过困难值小于等于\(x\)的路径所能到达的山峰中第\(k\)高的山峰,如果无解输出\(-1\). kruskal重构树是用来解决一些诸如"查询从某个点开始 经过边权不超过\(val\)所

【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树

这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,Lca只是他的一种应用,他可以搞各种树上问题,树上倍增一般都会用到f数组. |||.我们跑出来dfs序就能在他的上面进行主席树了. IV.别忘了离散. V.他可能不连通,我一开始想到了,但是我觉得出题人可能会是好(S)人(B),但是...... #include <cstdio> #include

BZOJ 3551 ONTAK2010 Peaks加强版 Kruskal重构树+可持久化线段树

题目大意:同3545 强制在线 3545题解传送门:http://blog.csdn.net/popoqqq/article/details/40660953 强制在线没法排序 启发式合并也就用不了了 Kruskal重构树是个挺好玩的东西 可以拿来处理一些最小生成树的边权最值问题 这里我们Kruskal连边时并不直接连边 而是新建一个节点ext 将两个点所在子树都连到ext的儿子上 比如说样例的树就建成了这样 图中红色的是原图的边权,黑色的是原图上的点 这样生成的树有一些十分优美的性质: 1.二

[IOI2018] werewolf 狼人

[IOI2018] werewolf 狼人 IOI2018题解 代码: #include<bits/stdc++.h> #define reg register int #define il inline #define numb (ch^'0') using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(

Kruskal重构树

不支持时间旅行的可持久化并查集 给定 n 个点, 以及 m 次操作, 操作有两种: ① 将点 x 与点 y 进行连边; ② 询问在前 t 次操作操作中, x 与 y 是否连通. n <= 100000, 强制在线. 核心模型 n 个点, m 条带权边的无向图. 多次询问点 x 和点 y 在边权不超过 w 的边的作用下的连通性信息(例如, 是否连通). 强制在线. 对于不支持时间旅行的并查集问题, 将时间这一维给量化之后, 可以等价地看成这个核心模型. 对于操作①, 我们相当于连边 (x, y)

算法学习——kruskal重构树

kruskal重构树是一个比较冷门的数据结构. 其实可以看做一种最小生成树的表现形式. 在普通的kruskal中,如果一条边连接了在2个不同集合中的点的话,我们将合并这2个点所在集合. 而在kruskal重构树中,如果一条边连接了在2个不同集合中的点,我们将新建一个节点出来,并用这个新节点作为一个中转连接这2个集合. 如图就是一棵kruskal重构树,方点表示新建出的节点,圆点是原图中的点,方点点权即边权. 这样建出的树会有一些美妙的性质,例如往上走点权是递增的,原图中的每个点都是叶子节点等.

Gym - 101173H Hangar Hurdles (kruskal重构树/最小生成树+LCA)

题目大意:给出一个n*n的矩阵,有一些点是障碍,给出Q组询问,每组询问求两点间能通过的最大正方形宽度. 首先需要求出以每个点(i,j)为中心的最大正方形宽度mxl[i][j],可以用二维前缀和+二分或者BFS求. 然后每相邻的两个点建一条权值为min(mxl[i][j],mxl[i'][j'])的边,求出整个图的最小生成树(注意边权要从大到小排序,实际上求出的是边权的“最大生成树”)或者kruskal重构树,对于每组询问(x1,y1),(x2,y2),答案为最小生成树上两点间路径的最小边权,或者

【BZOJ 3732】 Network Kruskal重构树+倍增LCA

Kruskal重构树裸题, Sunshine互测的A题就是Kruskal重构树,我通过互测了解到了这个神奇的东西... 理解起来应该没什么难度吧,但是我的Peaks连WA,,, 省选估计要滚粗了TwT #include<cstdio> #include<cstring> #include<algorithm> #define for1(i,a,n) for(int i=(a);i<=(n);i++) #define for2(i,a,n) for(int i=(a