1. 程式人生 > >【BZOJ3251】樹上三角形 暴力

【BZOJ3251】樹上三角形 暴力

include 結果 i++ nbsp ive 表示 tro 實測 還要

【BZOJ3251】樹上三角形

Description

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

Input

第一行兩個整數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

Output

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

Sample Input

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

Sample Output

N
Y
Y
N

HINT

對於100%的數據,n,q<=100000,點權範圍[1,231-1]

題解:正常人看到題,大概都會想到什麽樹剖+樹套樹套樹什麽的吧~

一種naive的做法就是,先將路徑上的所有數都拿出來排序,每次只需要判斷相鄰的三個數能否形成三角形就行了。

仔細觀察發現,如果答案為N,那麽最壞的情況,就是在排完序後,任意相鄰的三個數都滿足x<y<z且x+y=z。這不就是斐波那契數列嗎?

有什麽用呢?

斐波那契數列的增長不是指數級的嗎?

也就意味著一旦路徑的長度>logn(實測f(47)>2147483647,所以取47或50即可),我們的結果就是Y。

難道我們還要用倍增求出路徑長度嗎?

樸素LCA就行辣!一旦跑了50次,就直接輸出Y。否則就將所有數拿出來,用naive的做法搞一下就行了。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=100010;
int n,m,sum,cnt;
int to[maxn<<1],next[maxn<<1],head[maxn];
int fa[maxn],dep[maxn],v[maxn],p[60];
int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
void dfs(int x)
{
	for(int i=head[x];i!=-1;i=next[i])	fa[to[i]]=x,dep[to[i]]=dep[x]+1,dfs(to[i]);
}
void add(int a,int b)
{
	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
int main()
{
	n=rd(),m=rd();
	int i,j,a,b,c;
	for(i=1;i<=n;i++)	v[i]=rd();
	memset(head,-1,sizeof(head));
	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b);
	dep[1]=1,dfs(1);
	for(i=1;i<=m;i++)
	{
		c=rd(),a=rd(),b=rd(),sum=0;
		if(c)
		{
			v[a]=b;
			continue;
		}
		if(dep[a]<dep[b])	swap(a,b);
		while(dep[a]>dep[b]&&sum<50)	p[++sum]=v[a],a=fa[a];
		while(a!=b&&sum<50)	p[++sum]=v[a],p[++sum]=v[b],a=fa[a],b=fa[b];
		p[++sum]=v[a];
		if(sum>=50)
		{
			printf("Y\n");
			continue;
		}
		sort(p+1,p+sum+1);
		for(j=3;j<=sum;j++)
		{
			if(p[j]-p[j-1]<p[j-2])
			{
				printf("Y\n");
				break;
			}
		}
		if(j>sum)	printf("N\n");
	}
	return 0;
}

【BZOJ3251】樹上三角形 暴力