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

14 篇文章 0 订阅
8 篇文章 0 订阅
洛谷传送门
BZOJ传送门

题目描述

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

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

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

为了体现程序的在线性,我们把输入数据进行了加密。设 l a s t a n s lastans lastans为程序上一次输出的结果,初始的时候 l a s t a n s lastans lastans 0 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 xor运算符。

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

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

输入输出格式

输入格式:

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

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

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

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

接下来 T 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 lastans=0,所以真实操作为Q 8^0 7^0 3^0,也即Q 8 7 3。点 8 8 8到点 7 7 7的路径上一共有 5 5 5点,其权值为4 1 1 2 4
这些权值中,第三小的为 2 2 2,输出 2 2 2 l a s t a n s lastans lastans变为 2 2 2

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

img

解题分析

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

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

总复杂度 O ( N l o g 2 ( N ) ) O(Nlog^2(N)) O(Nlog2(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);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值