树上三角形(triangle) 基于LCT上的暴力

题目大意

给你一颗 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 每个节点的权值 <=2311

解题思路

对于 Ord=1 , 看到三角形自然就想到了 Fibonacci , 因为假如要满足没有三条边可以组成三角形的话最密集的情况就是 Fi=Fi1+Fi2 而每个数又小于 231 ,所以当路径上有超过50个点是就可以视为肯定存在三角形。否则就把路径上的点拿出来排序暴力判断即可。
对于 Ord=2 , 直接修改权值
对于 Ord=3 , 直接拿 LCT 维护就可以了, 操作都比较裸。

一些值得注意的地方

  1. 这道题在 Ord=3 时, 判断子树关系时要小心, 要以 1 为根判断。
  2. 在判断三角形是要开 longlong

程序

//树上三角形(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);
    }
}

你可能感兴趣的:(树上三角形(triangle) 基于LCT上的暴力)