您的位置:首页 > 其它

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

2016-03-20 08:50 239 查看

题目大意

给你一颗NN个节点以11为根的树,每个点有个正整数权值。

现在有MM个询问,每组询问有三个数,OrdOrd,aa,bb。OrdOrd表示要求操作的类型

每种操作为以下几种类型之一:

Ord=1Ord = 1 : 询问树上所有在aa到bb的简单路径的节点(含aa,bb)中,是否存在三个不同的节点,使得以这三个节点的点权为边长的三条边能够构成一个三角形。

Ord=2Ord = 2 : 将节点aa的权值改成bb

Ord=3Ord = 3 : 若节点bb不在以节点aa为根的子树里,那么令aa的父节点为bb,否则令bb的父节点为aa,如果a=ba = b那么忽略这一条操作。

所有操作按输入的顺序进行

N<=100000N<=100000 M<=200000M<=200000 每个节点的权值<=231−1<=2^{31}-1

解题思路

对于Ord=1Ord = 1, 看到三角形自然就想到了FibonacciFibonacci, 因为假如要满足没有三条边可以组成三角形的话最密集的情况就是Fi=Fi−1+Fi−2F_i = F_{i-1} + F_{i-2}而每个数又小于2312^{31},所以当路径上有超过50个点是就可以视为肯定存在三角形。否则就把路径上的点拿出来排序暴力判断即可。

对于Ord=2Ord = 2, 直接修改权值

对于Ord=3Ord = 3, 直接拿LCTLCT维护就可以了, 操作都比较裸。

一些值得注意的地方

这道题在Ord=3Ord = 3时, 判断子树关系时要小心, 要以11为根判断。

在判断三角形是要开longlonglong long

程序

//树上三角形(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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: