JZOJ4090. 树上三角形

题目大意

给定一棵 n 个节点树(根节点是1),每个点有点权vi,给定 q 个询问,要求支持三种操作。

  1. 1 u v 询问树上所有在u到v的简单路径的节点(含u,v)中,是否存在三个不同的节点,使得以这三个节点的点权为边长的三条边能够构成一个三角形。
  2. 2 u v 将节点u的权值改成v。
  3. 3 u v 若节点v不在以节点u为根的子树里,那么令u的父节点为v,否则令v的父节点为u,如果u=v那么忽略这一条操作。

Data Constraint
n100000,m200000,vi2311

题解

因为有换根操作,所以考虑LCT解决。操作都比较简单,就是在以1为根的树中判断包含关系,然后Cut,Link
再考虑什么情况下能构成三角形。如果给定一个序列不能构成三角形,那么最密集的形式必然是斐波那契数列,而 vi2311 ,所以如果序列长度大于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 ;
}

以上.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值