洛谷传送门
BZOJ传送门
题目描述
小Z有一片森林,含有 N N N个节点,每个节点上都有一个非负整数作为权值。初始的时候,森林中有 M M M条边。
小Z希望执行 T T T个操作,操作有两类:
Q x y k
查询点 x x x到点 y y y路径上所有的权值中,第 k k k小的权值是多少。此操作保证点 x x x和点 y y y连通,同时这两个节点的路径上至少有 k k k个点。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,T≤8∗104.
输入输出格式
输入格式:
第一行包含一个正整数 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 1≤testcase≤20。
第二行包含三个整数 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。之后的操作类似。
解题分析
首先我们看到了 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);
}
}
}