Luogu3302 [SDOI2013]森林

题目蓝链

Description

给你一个森林,你需要支持两个操作:

  1. 查询一条路径上第\(k\)小的权值是多少
  2. 连接两个点

强制在线

Solution

我们一开始看到这道题,一定会想什么LCT套主席树 乱编的

其实我们只需要主席树就可以了。我们发现这题只需要连边,不需要断边。所以我们可以启发式合并,暴力维护较小的那部分的主席树和倍增数组

时间复杂度\(\mathcal{O}(n \log^2 n)\)

如果你还不会用主席树维护链上第\(k\)大,请右转 传送门

Solution

#include <bits/stdc++.h>

using namespace std;

#define fst first
#define snd second
#define mp make_pair
#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef pair<int, int> pii;

template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }

inline int read() {
    int sum = 0, fg = 1; char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == ‘-‘) fg = -1;
    for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
    return fg * sum;
}

const int maxn = 8e4 + 10;

int n, m, q, len;
int w[maxn], t[maxn];
vector<int> g[maxn];

int Fa[maxn], sz[maxn];
inline int find(int x) { return x == Fa[x] ? x : Fa[x] = find(Fa[x]); }
inline void merge(int x, int y) { x = find(x), y = find(y), Fa[x] = y, sz[y] += x; }

int rt[maxn];
namespace ST {
    int cnt;
    struct node {
        int ls, rs, v;
    }A[maxn << 9];
    void init() { cnt = 0; }
    void change(int &nrt, int rt, int l, int r, int x) {
        A[nrt = ++cnt] = A[rt], ++A[nrt].v;
        if (l == r) return;
        int mid = (l + r) >> 1;
        if (x <= mid) change(A[nrt].ls, A[rt].ls, l, mid, x);
        else change(A[nrt].rs, A[rt].rs, mid + 1, r, x);
    }
    int query(int a, int b, int c, int d, int l, int r, int k) {
        if (l == r) return l;
        int mid = (l + r) >> 1;
        int tot = A[A[a].ls].v + A[A[b].ls].v - A[A[c].ls].v - A[A[d].ls].v;
        if (k <= tot) return query(A[a].ls, A[b].ls, A[c].ls, A[d].ls, l, mid, k);
        return query(A[a].rs, A[b].rs, A[c].rs, A[d].rs, mid + 1, r, k - tot);
    }
}

int d[maxn], fa[maxn][17];

inline int lca(int x, int y) {
    if (d[x] < d[y]) swap(x, y);
    for (int i = 16; ~i; i--)
        if (d[fa[x][i]] >= d[y]) x = fa[x][i];
    if (x == y) return x;
    for (int i = 16; ~i; i--)
        if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}

bool v[maxn];
inline void dfs(int now, int f) {
    v[now] = 1, fa[now][0] = f, d[now] = d[f] + 1;
    for (int i = 1; i <= 16; i++) fa[now][i] = fa[fa[now][i - 1]][i - 1];
    ST::change(rt[now], rt[f], 1, len, w[now]);
    for (int son : g[now]) {
        if (son == f) continue;
        dfs(son, now);
    }
}

int main() {
#ifdef xunzhen
    freopen("forest.in", "r", stdin);
    freopen("forest.out", "w", stdout);
#endif

    n = read();
    n = read(), m = read(), q = read();

    for (int i = 1; i <= n; i++) Fa[i] = i, sz[i] = 1;

    for (int i = 1; i <= n; i++) t[i] = w[i] = read();
    sort(t + 1, t + n + 1);
    len = unique(t + 1, t + n + 1) - t - 1;
    for (int i = 1; i <= n; i++) w[i] = lower_bound(t + 1, t + len + 1, w[i]) - t;

    for (int i = 1; i <= m; i++) {
        int x = read(), y = read();
        g[x].push_back(y);
        g[y].push_back(x);
        merge(x, y);
    }

    for (int i = 1; i <= n; i++) if (!v[find(i)]) dfs(find(i), 0);

    int lans = 0;
    for (int i = 1; i <= q; i++) {
        static char op[10];
        scanf("%s", op);
        if (op[0] == ‘Q‘) {
            int x = read() ^ lans, y = read() ^ lans, k = read() ^ lans, f = lca(x, y);
            printf("%d\n", lans = t[ST::query(rt[x], rt[y], rt[f], rt[fa[f][0]], 1, len, k)]);
        } else {
            int x = read() ^ lans, y = read() ^ lans;
            if (sz[x] > sz[y]) swap(x, y);
            g[x].push_back(y), g[y].push_back(x);
            merge(x, y), dfs(x, y);
        }
    }

    return 0;
}

原文地址:https://www.cnblogs.com/xunzhen/p/10322505.html

时间: 2024-10-19 09:38:57

Luogu3302 [SDOI2013]森林的相关文章

Luogu_P3302 [SDOI2013]森林【题解】主席树 lca 启发式合并

# Luogu_P3302 [SDOI2013]森林 主席树,启发式合并,lca luogu题面 求树上路径的第k大,树之间还有合并. 明显是主席树再加合并. 先说链上第k大,其实就是$Tx+Ty-Tlca-Tlcafa$ $T$表示权值线段树. 主席树维护的是从根节点到当前节点的前缀和. ask的代码如下: inline int ask(int x,int y,int lcc,int lcf,int l,int r,int k){ if(l==r) return b[l]; int lz=su

[SDOI2013]森林(树上主席树)

[SDOI2013]森林(luogu) Description 题目描述 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有M条边. 小Z希望执行T个操作,操作有两类: Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少.此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点. L x y在点x和点y之间连接一条边.保证完成此操作后,仍然是一片森林. 为了体现程序的在线性,我们把输入数据进行了加密.设lastans为程序上一次输出的结果,

[Sdoi2013]森林

/* 平常这种题很常见的思路就是求出dfs序来,然后每次查询的时候就是在主席树上查询 x+y-lca-fa[lca] 的值就行了. 但是这个题要动态的给森林中加边,还是强制在线的,所以就需要考虑换一种方法来维护这个东西. 首先先dfs出每棵树来,然后对于link操作,可以启发式合并两个主席树.这里我们把主席树维护的dfs序变成维护每个点到根的这条路径.所里link的时候假设我们要把x合到y上,那么我们就边dfs x 这棵树,边用当前点的fa作为历史状态的root来更新当前点的root就行了.求l

BZOJ 3123 SDOI2013 森林 可持久化线段树+倍增LCA+启发式合并

题目大意:给定一棵森林,每个点有权值,提供两种操作: 1.查询两点间路径上第k小的权值 2.将两个点之间连一条边 保证连接后仍是一座森林 可持久化线段树部分同Count On A Tree 只是这道题加了个连接操作 对于连接操作我们要用到启发式合并 就是把小的那棵树暴力重建 很简单的一个操作但是可以证明是均摊O(nlogn)的 大小我用了并查集 其实记录根就可以了 此外本题的多组数据是在逗比 记住testcase恒等于1就是了 NND我倍增LCA又写错了0.0 预处理时居然从大往小写的0.0 样

【主席树 启发式合并】bzoj3123: [Sdoi2013]森林

小细节磕磕碰碰浪费了半个多小时的时间 Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数表示 N个节点上的权值.  接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为“Q x y k”或者“L x y ”,其含义见题目描述部分. Output 对于每

P3302 [SDOI2013]森林

传送门 看到森林有合并首先会想到 $LCT$ ,然后发现链上第 $K$ 小不可维护 感觉 $LCT$ 只维护合并也有点大材小用了,考虑合并时直接启发式合并就可以不用 $LCT$ 然后求第 $K$ 小显然考虑主席树 对每个节点维护一个主席树,维护它到树根这的一段区间,那么当前节点的线段树可以直接借用父节点的线段树 并且因为主席树是可加减的,设节点 $x$ 的线段树为 $T[x]$,那么询问是就是在 $T[x]+T[y]-T[lca(x,y)]-T[fa[lca(x,y)]]$ 上面跑 每次启发式合

【主席树启发式合并】【P3302】[SDOI2013]森林

Description 给定一个 \(n\) 个节点的森林,有 \(Q\) 次操作,每次要么将森林中某两点联通,保证操作后还是个森林,要么查询两点间权值第 \(k\) 小,保证两点联通.强制在线. Limitation \(1~\leq~n,~Q~\leq~80000\) Solution 考虑有连边还有查询链上第 \(k\) 大,于是要么用 LCT,要么用主席树. 考虑如果用 LCT 的话,并不能快速的维护两点间链的信息(其实感觉在access的时候乱搞一下有希望在多一个 \(\log\) 的

[SDOI2013]森林(启发式合并)(主席树)

题目描述 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有M条边. 小Z希望执行T个操作,操作有两类: Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少.此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点. L x y在点x和点y之间连接一条边.保证完成此操作后,仍然是一片森林. 为了体现程序的在线性,我们把输入数据进行了加密.设lastans为程序上一次输出的结果,初始的时候lastans为0. 对于一个输入的操作Q x y k

「Luogu P3302」[SDOI2013]森林

给出一片森林,每个点有一个权值,要求支持动态连边,并回答任意两点间第 k 小权值,强制在线.\((1\le N,M,T \le 8\times 10^4)\) Luogu 分析 求第 k 小权值,这个肯定是用主席树了,但连边该怎么办?LCT?可我不会. 我们可以用启发式合并的方法,连边也就是合并两棵树,我们每次将较小的树连到较大的树上去,更新信息就暴力 dfs 较小树中的每一个点就好了. 代码 #include <bits/stdc++.h> #define N 80003 #define D