BZOJ 3251: 树上三角形

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,2^31-1]

题解

感觉非常奇怪,一道结论题。

要求简单路径还有路径上每个点的权值,所以不能用倍增,只可以一个一个搞。对于所有权值,排序以下就可以最坏O(n)的情况下算出是否可构成三角形。这样做有30分。

正解需要一个结论,因为点权在int范围(c++),所以都没有三角型的情况最多为1,1,2,3,5……即斐波那契数列,然而在int范围内大约有40多50个数。所以只要点数大约大于45即可认为有三角形。在暴力中找权值是加上该判断即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#define MAXN 100002
#define ll long long
using namespace std;
int n,m,zz,head[MAXN];
struct bian{int to,nx;} e[MAXN*2];
int h[MAXN],fa[MAXN];//2^17>10^5
ll a[MAXN],v[MAXN];
int top;
void insert(int x,int y)
{
	zz++; e[zz].to=y; e[zz].nx=head[x]; head[x]=zz;
	zz++; e[zz].to=x; e[zz].nx=head[y]; head[y]=zz;
}
void init()
{
	scanf("%d%d",&n,&m);
	int i,x,y;
	for(i=1;i<=n;i++) scanf("%lld",&v[i]);
	for(i=1;i<n;i++)
	   {scanf("%d%d",&x,&y);
	    insert(x,y);
	   }
}
//------------------------------------------------------------
void dfs(int x)
{
	int i,p;
	for(i=head[x];i;i=e[i].nx)
	   {p=e[i].to;
	    if(p==fa[x]) continue;
		fa[p]=x; h[p]=h[x]+1;
		dfs(p); 
	   }
}
bool geta(int x,int y)
{
	if(h[x]<h[y]) swap(x,y);
	top=0;
	for(;h[x]>h[y];x=fa[x])
	   {a[++top]=v[x]; 
	    if(top>45) return true;
	   }
	while(x!=y)
	   {a[++top]=v[x]; x=fa[x];
	    a[++top]=v[y]; y=fa[y];
	    if(top>45) return true;
	   }
	a[++top]=v[x];
	if(top>45) return true;
	sort(a+1,a+top+1);
	for(int i=1;i<top-1;i++)
	   {if(a[i]+a[i+1]>a[i+2]) return true;}
	return false;
}
void work()
{
	int i,x,y,t;
	for(i=1;i<=m;i++)
	   {scanf("%d%d%d",&t,&x,&y);
	    if(t==0)
	       {if(x==y||x==fa[y]||y==fa[x]) {printf("N\n"); continue;}
			if(geta(x,y)) printf("Y\n");
			else printf("N\n");
		   }
	    else v[x]=y;
	   }
}
int main()
{
	freopen("sdtg.in","r",stdin);
	freopen("sdtg.out","w",stdout);
	init(); dfs(1); work();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值