BZOJ3251 树上三角形

本文介绍了一种解决 BZOJ3251 问题的方法,通过优化暴力算法并利用斐波那契数列特性,解决了路径上点权是否能构成三角形的问题。文中还详细介绍了使用倍增法求解最近公共祖先(LCA)的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Address


Solution

  • 正解没想出来还把 LCA 写挂了,实在太弱了。
  • 先考虑一种暴力,把 uv u → v 路径上所有点权取出,按从小到大排序,则我们只需要判断相邻的三个元素是否能构成三角形即可。
  • 一个简单的解释:
    • 假设排序完后的序列为 a1,a2,...,am a 1 , a 2 , . . . , a m ,枚举三角形的最长边 ak a k
    • 那么一个合法的三角形显然满足 ai<aj<ak a i < a j < a k ai+aj>ak a i + a j > a k
    • 因为我们已经排好序了,所以 ak2,ak1 a k − 2 , a k − 1 显然比 ak a k 小,并且它们相加的和最大。
  • 暴力复杂度 O(q nlogn) O ( q   n log ⁡ n ) ,考虑怎样优化。
  • 根据暴力构造一组最长的不能构成三角形的序列 a1,a2,...,am a 1 , a 2 , . . . , a m (按照从小到大排序)。
  • 假定 a1=1,a2=1 a 1 = 1 , a 2 = 1 ,因为 a3a1+a2 a 3 ≥ a 1 + a 2 且序列要尽量长,所以 a3=a1+a2=2 a 3 = a 1 + a 2 = 2
  • 类似的,我们发现这样构造出来的就是斐波拉契数列,即 ai=ai1+ai2 a i = a i − 1 + a i − 2
  • 因为 1ai2311 1 ≤ a i ≤ 2 31 − 1 ,序列长度最多也就47。
  • 那么对于长度超过 47 的路径就可以直接输出 Y ‘ Y ′ ,这便是暴力的优化。
  • 用倍增求 LCA 的话,时间复杂度大概是 O(qlogn) O ( q log ⁡ n )

Code

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cctype>

using namespace std;

namespace inout
{
    const int S = 1 << 20;
    char frd[S], *ihed = frd + S;
    const char *ital = ihed;

    inline char inChar()
    {
        if (ihed == ital)
            fread(frd, 1, S, stdin), ihed = frd;
        return *ihed++;
    }

    inline int get()
    {
        char ch; int res = 0; bool flag = false;
        while (!isdigit(ch = inChar()) && ch != '-');
        (ch == '-' ? flag = true : res = ch ^ 48);
        while (isdigit(ch = inChar()))
            res = res * 10 + ch - 48;
        return flag ? -res : res; 
    }   
};
using namespace inout;

typedef long long ll;
const int N = 1e5 + 5;
int fa[N][20], a[N], val[N], dep[N];
int n, m, q;

struct Edge
{
    int to; Edge *nxt;
}p[N], *T = p, *lst[N];

inline void Link(int x, int y)
{
    (++T)->nxt = lst[x]; lst[x] = T; T->to = y;
}

inline void initLCA(int x, int Fa)
{
    dep[x] = dep[Fa] + 1;
    for (int i = 0; i < 17; ++i)
        fa[x][i + 1] = fa[fa[x][i]][i];

    for (Edge *e = lst[x]; e; e = e->nxt)
    {
        int y = e->to;
        fa[y][0] = x;
        initLCA(y, x);
    }   
}

inline int queryLCA(int x, int y)
{
    if (x == y) return x;
    if (dep[x] < dep[y]) swap(x, y);
    for (int i = 18; i >= 0; --i)
    {
        if (dep[fa[x][i]] >= dep[y]) x = fa[x][i];
        if (x == y) return x;
    }
    for (int i = 18; i >= 0; --i)
        if (fa[x][i] != fa[y][i])
            x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}

int main()
{
//  freopen("tri.in", "r", stdin);
//  freopen("tri.out", "w", stdout);

    n = get(); q = get(); int k, x, y, z;
    for (int i = 1; i <= n; ++i) val[i] = get();
    for (int i = 1; i < n; ++i)
    {
        x = get(); y = get();
        Link(x, y);
    }

    initLCA(1, 0);
    while (q--)
    {
        k = get(); x = get(); y = get();
        if (k & 1) val[x] = y;
        else 
        {
            z = queryLCA(x, y); bool flag = false;
            if (dep[x] + dep[y] - 2 * dep[z] > 46)
            {
                puts("Y");
                continue;
            } 
            a[m = 1] = val[z]; 
            while (x != z) a[++m] = val[x], x = fa[x][0];
            while (y != z) a[++m] = val[y], y = fa[y][0];
            sort(a + 1, a + m + 1);
            for (int i = 1, im = m - 2; i <= im; ++i)
                if (a[i] > a[i + 2] - a[i + 1])
                {
                    flag = true;
                    break;
                }
            puts(flag ? "Y" : "N"); 
        }
    }

//  fclose(stdin); fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值