洛谷传送门
BZOJ传送门
题目描述
小Z有一片森林,含有 个节点,每个节点上都有一个非负整数作为权值。初始的时候,森林中有 条边。
小Z希望执行 个操作,操作有两类:
Q x y k
查询点 到点 路径上所有的权值中,第 小的权值是多少。此操作保证点 和点 连通,同时这两个节点的路径上至少有 个点。L x y
在点 和点 之间连接一条边。保证完成此操作后,仍然是一片森林。
为了体现程序的在线性,我们把输入数据进行了加密。设 为程序上一次输出的结果,初始的时候 为 。
- 对于一个输入的操作
Q x y k
,其真实操作为Q x^lastans y^lastans k^lastans
。 - 对于一个输入的操作
L x y
,其真实操作为L x^lastans y^lastans
。其中^
运算符表示异或,等价于pascal中的 运算符。
请写一个程序來帮助小Z完成这些操作。
对于所有的数据, .
输入输出格式
输入格式:
第一行包含一个正整数 ,表示当前测试数据的测试点编号。保证 。
第二行包含三个整数 , , ,分别表示节点数、初始边数、操作数。
第三行包含 个非负整数表示 个节点上的权值。
接下来 行,每行包含两个整数 和 ,表示初始的时候,点 和点 之间有一条无向边。
接下来
行,每行描述一个操作,格式为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
,此时
,所以真实操作为Q 8^0 7^0 3^0
,也即Q 8 7 3
。点
到点
的路径上一共有
点,其权值为4 1 1 2 4
。
这些权值中,第三小的为
,输出
,
变为
。
对于第二个操作 Q 3 5 1
,此时
,所以真实操作为Q 3^2 5^2 1^2
,也即Q 1 7 3
。点
到点
的路径上一共有
个点,其权值为1 1 2 4
。
这些权值中,第三小的为
,输出
,
变为
。之后的操作类似。
解题分析
首先我们看到了 操作, 想到 , 但是链上 大这种操作似乎 不资瓷? 只好弃掉…
那么什么 资瓷这个操作? 显然是主席树。 合并怎么办?没事我们还有启发式合并, 无非加个 罢了。每次暴力 重构联通块小的那个子树, 用 或倍增 维护联通快内的 。
总复杂度 。
代码如下:
#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);
}
}
}