给你一颗 N 个节点以 1 为根的树,每个点有个正整数权值。
现在有 M 个询问,每组询问有三个数, Ord , a , b 。 Ord 表示要求操作的类型
每种操作为以下几种类型之一:
Ord=1 : 询问树上所有在 a 到 b 的简单路径的节点(含 a , b )中,是否存在三个不同的节点,使得以这三个节点的点权为边长的三条边能够构成一个三角形。
Ord=2 : 将节点 a 的权值改成 b
Ord=3 : 若节点 b 不在以节点 a 为根的子树里,那么令 a 的父节点为 b ,否则令 b 的父节点为 a ,如果 a=b 那么忽略这一条操作。
所有操作按输入的顺序进行
N<=100000 M<=200000 每个节点的权值 <=231−1
对于 Ord=1 , 看到三角形自然就想到了 Fibonacci , 因为假如要满足没有三条边可以组成三角形的话最密集的情况就是 Fi=Fi−1+Fi−2 而每个数又小于 231 ,所以当路径上有超过50个点是就可以视为肯定存在三角形。否则就把路径上的点拿出来排序暴力判断即可。
对于 Ord=2 , 直接修改权值
对于 Ord=3 , 直接拿 LCT 维护就可以了, 操作都比较裸。
//树上三角形(triangle) YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 100005;
int N, M, top, Size[MAXN], D[MAXN], Pre[MAXN], Son[MAXN][2];
long long Val[MAXN];
bool Rev[MAXN];
void Updata(int Now) {
Size[Now] = 1 + Size[Son[Now][0]] + Size[Son[Now][1]];
}
bool IsRoot(int Now) { return Son[Pre[Now]][0] != Now && Son[Pre[Now]][1] != Now;}
void Reverse(int Now) {
if (Rev[Now]) {
Rev[Son[Now][0]] ^= 1, Rev[Son[Now][1]] ^= 1;
swap(Son[Now][0], Son[Now][1]);
Rev[Now] = 0;
}
}
void Rotate(int Now) {
int Fa = Pre[Now], Gran = Pre[Fa], Side = (Son[Fa][1] == Now);
if (!IsRoot(Fa)) Son[Gran][Son[Gran][1] == Fa] = Now;
Pre[Fa] = Now, Pre[Now] = Gran;
Son[Fa][Side] = Son[Now][!Side], Pre[Son[Fa][Side]] = Fa;
Son[Now][!Side] = Fa;
Updata(Fa), Updata(Now);
}
void Splay(int Now) {
static int D[MAXN], top;
D[top = 1] = Now;
for (int p = Now; !IsRoot(p); p = Pre[p]) D[++ top] = Pre[p];
for (; top; top --) Reverse(D[top]);
for (; !IsRoot(Now); Rotate(Now)) {
int Fa = Pre[Now], Gran = Pre[Fa];
if (IsRoot(Fa)) continue;
(Son[Fa][0] == Now) ^ (Son[Gran][0] == Fa) ? Rotate(Now) : Rotate(Fa);
}
Updata(Now);
}
void Access(int Now) {
for (int t = 0; Now; Son[Now][1] = t, t = Now, Now = Pre[Now]) Splay(Now);
}
void MakeRoot(int Now) {
Access(Now); Splay(Now); Rev[Now] ^= 1;
}
void Query(int u, int v) {
MakeRoot(u), Access(v), Splay(v);
}
void Cut(int Now) {
Access(Now), Splay(Now);
Pre[Son[Now][0]] = Son[Now][0] = 0;
}
void Link(int u, int v) {
MakeRoot(v); Pre[v] = u;
}
void Modify(int u, int v) {
if (u == v) return;
MakeRoot(1), Access(v), Splay(v);
int Now = u;
while (!IsRoot(Now)) Now = Pre[Now];
if (Now == v) Cut(v), Link(u, v); else Cut(u), Link(v, u);
}
void GetAll(int Now) {
if (!Now) return;
D[++ top] = Now;
GetAll(Son[Now][0]), GetAll(Son[Now][1]);
}
bool cmp(int u, int v) { return Val[u] < Val[v];}
void Triangle(int u, int v) {
Query(u, v);
if (Size[v] < 3) {printf("N\n"); return;}
if (Size[v] >= 100) { printf("Y\n"); return;}
top = 0;
GetAll(v);
sort(D + 1, D + 1 + Size[v], cmp);
for (int i = 3; i <= Size[v]; i ++) {
if (Val[D[i - 1]] + Val[D[i - 2]] > Val[D[i]]) {
printf("Y\n");
return;
}
}
printf("N\n");
}
int main() {
freopen("triangle.in", "r", stdin), freopen("triangle.out", "w", stdout);
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; i ++) scanf("%d", &Val[i]);
for (int i = 2; i <= N; i ++) {
int u;
scanf("%d", &u);
Pre[i] = u;
}
for (int i = 1; i <= M; i ++) {
int Ord, u, v;
scanf("%d%d%d", &Ord, &u, &v);
if (Ord == 1) Triangle(u, v);
if (Ord == 2) Val[u] = v;
if (Ord == 3) Modify(u, v);
}
}