题目大意
给定一棵
n
个节点树(根节点是1),每个点有点权
- 1 u v 询问树上所有在u到v的简单路径的节点(含u,v)中,是否存在三个不同的节点,使得以这三个节点的点权为边长的三条边能够构成一个三角形。
- 2 u v 将节点u的权值改成v。
- 3 u v 若节点v不在以节点u为根的子树里,那么令u的父节点为v,否则令v的父节点为u,如果u=v那么忽略这一条操作。
Data Constraint
题解
因为有换根操作,所以考虑LCT解决。操作都比较简单,就是在以1为根的树中判断包含关系,然后Cut,Link
再考虑什么情况下能构成三角形。如果给定一个序列不能构成三角形,那么最密集的形式必然是斐波那契数列,而
vi≤231−1
,所以如果序列长度大于50,这个序列一定能构成三角形。所以直接在LCT中取出来排序,暴力判断即可。
时间复杂度: O(nlog2n)
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
#define N 100000 + 10
typedef long long ll ;
bool Rev[N] ;
int Pre[N] , Son[N][2] ;
int Size[N] , D[N] ;
ll Val[N] , a[N] ;
int n , Q ;
bool IsRoot( int x ) { return Son[Pre[x]][0] != x && Son[Pre[x]][1] != x ; }
bool Exist( int x , int y ) {
while ( !IsRoot(x) ) x = Pre[x] ;
return x == y ;
}
void Push( int x ) {
if ( !Rev[x] ) return ;
swap( Son[x][0] , Son[x][1] ) ;
Rev[Son[x][0]] ^= 1 ;
Rev[Son[x][1]] ^= 1 ;
Rev[x] = 0 ;
}
void Update( int x ) {
Size[x] = Size[Son[x][0]] + Size[Son[x][1]] + 1 ;
}
void Rotate( int x ) {
int F = Pre[x] , G = Pre[F] ;
int Side = ( Son[F][1] == x ) ;
if ( !IsRoot(F) ) Son[G][Son[G][1] == F] = x ;
Pre[x] = G , Pre[F] = x ;
Son[F][Side] = Son[x][!Side] ;
Pre[Son[x][!Side]] = F ;
Son[x][!Side] = F ;
Update(F) ;
Update(x) ;
}
void Splay( int x ) {
D[D[0] = 1] = x ;
for (int now = x ; !IsRoot(now) ; now = Pre[now] ) D[++D[0]] = Pre[now] ;
for (int i = D[0] ; i ; i -- ) Push( D[i] ) ;
while ( !IsRoot(x) ) {
int y = Pre[x] , z = Pre[y] ;
if ( !IsRoot(y) ) {
if ( ( Son[y][0] == x ) ^ ( Son[z][0] == y ) ) Rotate(x) ;
else Rotate(y) ;
}
Rotate(x) ;
}
Update(x) ;
}
void Access( int x ) {
int last = 0 ;
while ( x ) {
Splay(x) ;
Son[x][1] = last ;
last = x ;
x = Pre[x] ;
}
}
void MakeRoot( int x ) {
Access(x) ;
Splay(x) ;
Rev[x] ^= 1 ;
}
void Cut( int u , int v ) {
MakeRoot(u) ;
Access(v) ;
Splay(v) ;
Pre[u] = Son[v][0] = 0 ;
}
void Link( int u , int v ) {
MakeRoot(v) ;
Pre[v] = u ;
}
void Query( int u , int v ) {
MakeRoot(u) ;
Access(v) ;
Splay(v) ;
}
int GetFa( int x ) {
Access(x) ;
Splay(x) ;
x = Son[x][0] ;
while ( Son[x][1] ) x = Son[x][1] ;
return x ;
}
void Solve( int u , int v ) {
if ( u == v ) return ;
MakeRoot(1) ;
Access(v) ;
Splay(v) ;
if ( Exist( u , v ) ) Cut( GetFa(v) , v ) , Link( u , v ) ;
else Cut( GetFa(u) , u ) , Link( v , u ) ;
}
void Find( int x ) {
if ( !x ) return ;
a[++a[0]] = Val[x] ;
Find( Son[x][0] ) ;
Find( Son[x][1] ) ;
}
void Calc( int u , int v ) {
Query( u , v ) ;
if ( Size[v] < 3 ) { printf( "N\n" ) ; return ; }
if ( Size[v] > 50 ) { printf( "Y\n" ) ; return ; }
a[0] = 0 ;
Find(v) ;
sort( a + 1 , a + a[0] + 1 ) ;
for (int i = 3 ; i <= a[0] ; i ++ ) {
if ( a[i-2] + a[i-1] > a[i] ) {
printf( "Y\n" ) ;
return ;
}
}
printf( "N\n" ) ;
}
int main() {
freopen( "triangle.in" , "r" , stdin ) ;
freopen( "triangle.out" , "w" , stdout ) ;
scanf( "%d%d" , &n , &Q ) ;
for (int i = 1 ; i <= n ; i ++ ) scanf( "%d" , &Val[i] ) ;
for (int i = 2 ; i <= n ; i ++ ) scanf( "%d" , &Pre[i] ) ;
for (int i = 1 ; i <= Q ; i ++ ) {
int op , u , v ;
scanf( "%d%d%d" , &op , &u , &v ) ;
if ( op == 1 ) Calc( u , v ) ;
else if ( op == 2 ) Val[u] = v ;
else Solve( u , v ) ;
}
return 0 ;
}
以上.