[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]

题目分析

贪心来想 假设你找到了一条最长路径,并且保证在它上面的点权都不能构成三角形。
那么将边权从大到小排序后 点权一定满足 a[i]+a[i+1]==a[i+2]
这是啥 斐波那契数列啊QAQ
我们再来看点权范围 [ int ] 在[ int ]范围内的斐波那契数列的个数为47
这说明:如果询问路径上的点的个数超过了47 那一定会能构成三角形
如果不超47直接暴力就好了 用朴素lca拿路径 如果当前栈内点数大于47直接return

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
int n,m;
int tot;
int head[110000],to[220000],net[220000];
void add(int x,int y)
{
    net[++tot]=head[x];
    to[tot]=y;
    head[x]=tot;
}
long long val[110000];
int deep[110000],fa[110000];
void dfs(int x)
{
    deep[x]=deep[fa[x]]+1;
    for(int i=head[x];i;i=net[i])
        if(to[i]!=fa[x])
            fa[to[i]]=x,dfs(to[i]);
}
long long sta[100];
int ask(int x,int y)
{
    sta[0]=0;
    while(sta[0]<50&&x!=y)
    {
        if(deep[x]>deep[y]) sta[++sta[0]]=val[x],x=fa[x];
        else sta[++sta[0]]=val[y],y=fa[y]; 
    }
    sta[++sta[0]]=val[x];
    if(sta[0]>50) return 1;
    sort(sta+1,sta+sta[0]+1);
    for(int i=1;i<=sta[0]-2;i++)
        if((long long)sta[i]+sta[i+1]>sta[i+2]) return 1;
    return 0; 
}
int q;
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) scanf("%lld",&val[i]);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    dfs(1);
    for(int i=1;i<=q;i++)
    {
        int tmp,x,y;
        long long c;
        scanf("%d%d",&tmp,&x);
        if(tmp==0) 
        {
            scanf("%d",&y);
            if(ask(x,y)) printf("Y\n");
            else printf("N\n");
        }
        else scanf("%lld",&c),val[x]=c;
    }
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注