2017-4-23 周考 T1 树上三角形

1. 题目描述

给定一个大小为 n 的有点权树,每次询问一对点(u,v),问是否能在 u 到 v 的简单路径上取三个点权,以这三个权值为边长构成一个三角形。同时还需支持单点修改。

2. 输入格式

第一行两个整数 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

3. 输出格式

对每个询问输出一行表示答案,“Y”表示有解,“N”表示无解。

4. 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

5. Sample Outout

N

Y

Y

N

6. 数据范围与约定

对于10%的数据,n,q100n,q≤100

对于30%的数据, n,q1000n,q≤1000

对于另外40%的数据,无修改操作

对于 100%的数据, n,q100000n,q≤100000,点权范围[1,231−1]

7. 思路

题目下来时有人说这题要 Tarjan,有说要倍增的,甚至有说要主席树的。

不过我构造了一下非法情况发现:这个题如果要造一个最长的非法链,那么链上的点的权值排序后一定是斐波那契数列。

为什么呢?

我们可以这样来看:

有三个数 a、b、c 已经按照升序排序,k为正数,那么根据不等式的性质可得

a-k+b>c 和 a+b>c+k 时,有 a+b>c

所以寻找是否有三个权值可以构成三角形只需要排序后检查相邻的三个值,比暴力找寻不知道高到哪里去了

因此一条最长的非法链权值肯定是斐波那契数列。

我们发现这道题的最大点权为231-1,因此一条非法链最大长度为46(再大就会有点权超出题目限制)。

(原谅我开了个挂,其实自己写也用不了什么时间)

稍微给点容差,就把长度大于50的链直接输出“Y”吧,其余的暴力。*笑*

至于找LCA/求链长用最水的一个就行了。

6. 代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <list>
#include <vector>

typedef long long ll;
using namespace std;

const int maxn=110000;
int n,q;
int w[maxn],dep[maxn],p[maxn];
vector<int> s[maxn];

void GetDep(int x){
    for(int i=0;i<s[x].size();i++){
        dep[s[x][i]]=dep[x]+1;
        p[s[x][i]]=x;
        GetDep(s[x][i]);
    }
    return;
}

int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        s[a].push_back(b);
    }
    dep[1]=1; p[1]=-1;
    GetDep(1);
    while(q--){
        int cmd,a,b;
        scanf("%d%d%d",&cmd,&a,&b);
        if(cmd==0){
            int len=0;
            vector<int> rd;
            while(len<50&&a!=b){
                if(dep[a]>dep[b]){
                    rd.push_back(w[a]);
                    a=p[a];
                    len++;
                }
                else{
                    rd.push_back(w[b]);
                    b=p[b];
                    len++;
                }
            }
            rd.push_back(w[a]); len++;
            if(len>=50) printf("Y\n");
            else{
                bool tri=false;
                sort(rd.begin(),rd.end());
                for(int i=0;i+2<rd.size();i++){
                    if(rd[i]>rd[i+2]-rd[i+1]){
                        tri=true;
                        break;
                    }
                }
                if(tri) printf("Y\n");
                else printf("N\n");
            }
            rd.clear();
        }
        else w[a]=b;
    }
    return 0;
}

 

posted @ 2017-04-25 20:23  VKorpela  阅读(168)  评论(0编辑  收藏  举报
知识共享许可协议
爱与包容记心中 · 友谊魔法永流传
加密文章的密码是最棒的小马名字(没有空格)