[Luogu P3302] [BZOJ 3123] [SDOI2013]森林

版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/82930253

洛谷传送门

BZOJ传送门

题目描述

小Z有一片森林,含有 N N 个节点,每个节点上都有一个非负整数作为权值。初始的时候,森林中有 M M 条边。

小Z希望执行 T T 个操作,操作有两类:

  1. Q x y k查询点 x x 到点 y y 路径上所有的权值中,第 k k 小的权值是多少。此操作保证点 x x 和点 y y 连通,同时这两个节点的路径上至少有 k k 个点。
  2. L x y在点 x x 和点 y y 之间连接一条边。保证完成此操作后,仍然是一片森林。

为了体现程序的在线性,我们把输入数据进行了加密。设 l a s t a n s lastans 为程序上一次输出的结果,初始的时候 l a s t a n s lastans 0 0

  • 对于一个输入的操作Q x y k,其真实操作为Q x^lastans y^lastans k^lastans
  • 对于一个输入的操作L x y,其真实操作为L x^lastans y^lastans。其中^运算符表示异或,等价于pascal中的 x o r xor 运算符。

请写一个程序來帮助小Z完成这些操作。

对于所有的数据, n , m , T 8 1 0 4 n,m,T\le 8*10^4 .

输入输出格式

输入格式:

第一行包含一个正整数 t e s t c a s e testcase ,表示当前测试数据的测试点编号。保证 1 t e s t c a s e 20 1\le testcase\le 20

第二行包含三个整数 N N M M T T ,分别表示节点数、初始边数、操作数。

第三行包含 N N 个非负整数表示 N N 个节点上的权值。

接下来 M M 行,每行包含两个整数 x x y y ,表示初始的时候,点 x x 和点 y y 之间有一条无向边。

接下来 T T 行,每行描述一个操作,格式为Q x y k或者L x y,其含义见题目描述部分。

输出格式:

对于每一个第一类操作,输出一个非负整数表示答案。

输入输出样例

输入样例#1:

1
8  4 8
1  1 2 2 3 3 4 4
4  7
1  8
2  4
2  1
Q 8 7 3 Q 3 5 1
Q 10 0 0
L 5 4
L 3 2 L 0 7
Q 9 2 5 Q 6 1 6

输出样例#1:

2 
2
1
4
2

说明

对于第一个操作 Q 8 7 3,此时 l a s t a n s = 0 lastans=0 ,所以真实操作为Q 8^0 7^0 3^0,也即Q 8 7 3。点 8 8 到点 7 7 的路径上一共有 5 5 点,其权值为4 1 1 2 4
这些权值中,第三小的为 2 2 ,输出 2 2 l a s t a n s lastans 变为 2 2

对于第二个操作 Q 3 5 1 ,此时 l a s t a n s = 2 lastans=2 ,所以真实操作为Q 3^2 5^2 1^2 ,也即Q 1 7 3。点 1 1 到点 7 7 的路径上一共有 4 4 个点,其权值为1 1 2 4
这些权值中,第三小的为 2 2 ,输出 2 2 l a s t a n s lastans 变为 2 2 。之后的操作类似。

img

解题分析

首先我们看到了 l i n k link 操作, 想到 L C T LCT , 但是链上 k k 大这种操作似乎 L C T LCT 不资瓷? 只好弃掉…

那么什么 d s ds 资瓷这个操作? 显然是主席树。 合并怎么办?没事我们还有启发式合并, 无非加个 l o g log 罢了。每次暴力 D F S DFS 重构联通块小的那个子树, 用 L C T LCT 或倍增 L C A LCA 维护联通快内的 L C A LCA

总复杂度 O ( N l o g 2 ( N ) ) O(Nlog^2(N))

代码如下:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100050
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
struct Node1 {int son[2], fat, rev;} t[MX];
struct Node2 {int son[2], sum;} tree[MX * 800];
struct Edge {int to, nex;} edge[MX << 2];
int bel[MX], sta[MX], val[MX], cpy[MX], id[MX], head[MX], root[MX], siz[MX];
int dot, dif, q, kth, cnt, arr, lastans, line, top;
IN void add(R int from, R int to) {edge[++arr] = {to, head[from]}, head[from] = arr;}
int find(R int now) {return bel[now] == now ? now : bel[now] = find(bel[now]);}
namespace LCT
{
    #define dad t[now].fat
    #define ls t[now].son[0]
    #define rs t[now].son[1]
    IN bool get(R int now) {return t[dad].son[1] == now;}
    IN bool nroot(R int now) {return t[dad].son[1] == now || t[dad].son[0] == now;}
    IN void pushrev(R int now) {std::swap(ls, rs), t[now].rev ^= 1;}
    IN void pushdown(R int now) {if(t[now].rev) pushrev(ls), pushrev(rs), t[now].rev = 0;}
    IN void rotate(R int now)
    {
        R int fa = dad, grand = t[fa].fat;
        R bool dir = get(now);
        t[fa].son[dir] = t[now].son[dir ^ 1];
        t[t[now].son[dir ^ 1]].fat = fa;
        t[now].fat = grand;
        if(nroot(fa)) t[grand].son[get(fa)] = now;
        t[now].son[dir ^ 1] = fa;
        t[fa].fat = now;
    }
    IN void splay(R int now)
    {
        R int tmp = now, fa;
        sta[top = 1] = now;
        W (nroot(now)) sta[++top] = now = dad;
        W (top) pushdown(sta[top--]);
        now = tmp;
        W (nroot(now))
        {
            fa = dad;
            if(nroot(fa)) rotate(get(now) == get(fa) ? fa : now);
            rotate(now);
        }
    }
    IN int access(R int now)
    {
        R int x;
        for (x = 0; now; x = now, now = dad)
        splay(now), rs = x;
        return x;
    }
    IN void makeroot(R int x) {access(x), splay(x), pushrev(x);}
    IN int query(R int x, R int y) {access(x); return access(y);}
    IN void link(R int x, R int y) {makeroot(x); t[x].fat = y;}
    #undef dad
    #undef ls
    #undef rs
}
namespace PT
{
    #define ls tree[now].son[0]
    #define rs tree[now].son[1]
    void build(int &now, R int lef, R int rig)
    {
        now = ++cnt;
        if(lef == rig) return;
        int mid = lef + rig >> 1;
        build(ls, lef, mid), build(rs, mid + 1, rig);
    }
    void insert(int &now, R int pre, R int lef, R int rig, R int tar)
    {
        now = ++cnt; tree[now] = tree[pre]; tree[now].sum++;
        if(lef == rig) return;
        int mid = lef + rig >> 1;
        if(tar <= mid) insert(ls, tree[pre].son[0], lef, mid, tar);
        else insert(rs, tree[pre].son[1], mid + 1, rig, tar);
    }
    int query(R int a1, R int a2, R int b1, R int b2, R int lef, R int rig)
    {
        if(lef == rig) return cpy[lef];
        int lsum = tree[tree[a1].son[0]].sum + tree[tree[a2].son[0]].sum - tree[tree[b1].son[0]].sum - tree[tree[b2].son[0]].sum;
        int mid = lef + rig >> 1;
        if(lsum >= kth) return query(tree[a1].son[0], tree[a2].son[0], tree[b1].son[0], tree[b2].son[0], lef, mid);
        else return kth -= lsum, query(tree[a1].son[1], tree[a2].son[1], tree[b1].son[1], tree[b2].son[1], mid + 1, rig);
    }
    void DFS(R int now, R int fa)
    {
        insert(root[now], root[fa], 1, dif, id[now]);
        for (R int i = head[now]; i; i = edge[i].nex)
        {
            if(edge[i].to == fa) continue;
            DFS(edge[i].to, now);
        }
    }
    IN void merge(R int x, R int y)
    {
        int belx = find(x), bely = find(y);
        if(siz[belx] > siz[bely]) std::swap(belx, bely), std::swap(x, y);
        siz[bely] += siz[belx]; bel[belx] = bely;
        LCT::link(x, y); add(x, y), add(y, x);
        DFS(x, y);
    }
    #undef ls
    #undef rs
}
int main(void)
{
    char buf[5];
    int a, b, lca, fa;
    in(dot); in(dot), in(line), in(q);
    for (R int i = 1; i <= dot; ++i) bel[i] = i, siz[i] = 1;
    for (R int i = 1; i <= dot; ++i) in(val[i]), cpy[i] = val[i];
    std::sort(cpy + 1, cpy + 1 + dot);
    dif = std::unique(cpy + 1, cpy + 1 + dot) - cpy - 1;
    for (R int i = 1; i <= dot; ++i) id[i] = std::lower_bound(cpy + 1, cpy + 1 + dif, val[i]) - cpy;
    PT::build(root[0], 1, dif);
    for (R int i = 1; i <= dot; ++i) PT::insert(root[i], root[0], 1, dif, id[i]);
    for (R int i = 1; i <= line; ++i)
    in(a), in(b), PT::merge(a, b);
    W (q--)
    {
        scanf("%s", buf);
        if(buf[0] == 'Q')
        {
            in(a), in(b), in(kth);
            a ^= lastans, b ^= lastans, kth ^= lastans;
            LCT::makeroot(find(a));//link 后可能信息不完整,随时makeroot
            lca = LCT::query(a, b);
            if(find(a) == lca) fa = 0;
            else
            {
                LCT::access(lca), LCT::splay(lca);
                fa = t[lca].son[0];
                W (t[fa].son[1]) fa = t[fa].son[1], LCT::pushdown(fa);
            }
            printf("%d\n", lastans = PT::query(root[a], root[b], root[lca], root[fa], 1, dif));
        }
        else
        {
            in(a), in(b);
            a ^= lastans, b ^= lastans;
            PT::merge(a, b);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/LPA20020220/article/details/82930253