树上三角形(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); } }
相关文章推荐
- Oracle数据库、实例、用户、表空间、表之间的关系
- Linux内核分析(四)
- FFmpeg教程(二)FFmpeg命令行工具的使用
- Python之反射
- 第39讲项目1——完数(2)
- #碰到的小问题#c++中vector<int> 和vector<int>::iterator有什么不同
- day01 html面试题--表单常用的提交方式有哪些?区别是
- Spring整合Hibernate图文步骤
- 谈谈C++类与主函数的结构安排
- Ecstore中如何调用发起Ajax请求
- 1、php基本语法--函数
- 自己做网站一定要加上的head代码(收集)
- 值传递,指针传值以及引用传值的区别
- 通过点击图标/按钮处理一级菜单的显示和隐藏
- bzoj 1537 [POI2005]Aut- The Bus(DP+BIT)
- 内核态(Kernel Mode)与用户态(User Mode)
- Ubuntu Server下启动/停止/重启MySQL数据库的三种方式
- Git学习笔记(1)
- iOS 国际化做法
- 2751: [HAOI2012]容易题(easy)|快速幂