1. 程式人生 > >【bzoj3251】樹上三角形 樸素LCA+暴力

【bzoj3251】樹上三角形 樸素LCA+暴力

dfs add swa head 輸出 div 斐波那契 sample std

題目描述

給定一大小為n的有點權樹,每次詢問一對點(u,v),問是否能在u到v的簡單路徑上取三個點權,以這三個權值為邊長構成一個三角形。同時還支持單點修改。

輸入

第一行兩個整數n、q表示樹的點數和操作數 第二行n個整數表示n個點的點權 以下n-1行,每行2個整數a、b,表示a是b的父親(以1為根的情況下) 以下q行,每行3個整數t、a、b 若t=0,則詢問(a,b) 若t=1,則將點a的點權修改為b

輸出

對每個詢問輸出一行表示答案,“Y”表示有解,“N”表示無解。

樣例輸入

5 5
1 2 3 4 5
1 2
2 3

3 4
1 5
0 1 3
0 4 5
1 1 4
0 2 5
0 2 3

樣例輸出

N
Y
Y
N


題解

樸素LCA+暴力

一開始想到了一個$O(n\log^3n)$的數據結構算法,然後發現自己太naive了= =

由於點權是int範圍內的,所以如果想讓盡量多的邊不構成三角形,那麽它們的邊權應該為1、1、2、3、5、8、...

這顯然是斐波那契數列,而斐波那契數列是指數增長的,到第50項左右就爆int了。

所以可以直接拿出兩個點之間的路徑,當拿出的超過50個時直接判定能構成三角形,否則排序,暴力。

時間復雜度為$O(q·50·\log 50)$

#include <cstdio>
#include <algorithm>
#define N 100010
using namespace std;
int w[N] , head[N] , to[N << 1] , next[N << 1] , cnt , fa[N] , deep[N] , a[100] , tot;
void add(int x , int y)
{
	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
	int i;
	for(i = head[x] ; i ; i = next[i])
		if(to[i] != fa[x])
			fa[to[i]] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
}
bool judge(int x , int y)
{
	int i;
	tot = 0;
	if(deep[x] < deep[y]) swap(x , y);
	while(deep[x] > deep[y])
	{
		a[++tot] = w[x] , x = fa[x];
		if(tot > 50) return 1;
	}
	while(x != y)
	{
		a[++tot] = w[x] , a[++tot] = w[y] , x = fa[x] , y = fa[y];
		if(tot > 50) return 1;
	}
	a[++tot] = w[x] , sort(a + 1 , a + tot + 1);
	for(i = 3 ; i <= tot ; i ++ )
		if(a[i] - a[i - 1] < a[i - 2])
			return 1;
	return 0;
}
int main()
{
	int n , m , i , opt , x , y;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &w[i]);
	for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x);
	dfs(1);
	while(m -- )
	{
		scanf("%d%d%d" , &opt , &x , &y);
		if(opt) w[x] = y;
		else if(judge(x , y)) puts("Y");
		else puts("N");
	}
	return 0;
}

【bzoj3251】樹上三角形 樸素LCA+暴力