2019 杭电 多校第二场 1011 Keen On Everything But Triangle

题目链接

题意 :

N条线段,Q次查询。查询的是区间内线段能组成的周长最长的三角形的周长。

分析:

首先,对于三条线组成周长最长三角形的问题,我们将线段排序后从大到小依次枚举,然后判断是否能构成三角形。值得注意的是,在最坏情况下我们也只需枚举几十次。因为最坏情况是斐波那契数列,然而斐波那契数列几十个之后就在数据范围之外了。既然是区间查询K大值问题,我们考虑使用主席树。每次区间查询最多需要查询到第几十大,所以时间完全足够。

出题方题解

首先考虑区间最大的三个数能否形成三角形,如果不能,考虑区间第 二大、第三大、第四大的三个数,以此类推,直到能形成三角形。由三角 形最小的两条边大于第三边的性质可知,只需要考虑区间的前嬴嬴 大数即可 (最坏情况下区间前几大数形成了斐波那契数列) 。 时间复杂度O(nlogn∗44) 。

代码

#include <stdio.h>
#include <algorithm>

using namespace std;

const int MAXN = 100010;
const int M = MAXN * 30;
int n, q, m, tot;
int a[MAXN], t[MAXN], T[MAXN], lson[M], rson[M], c[M];
int cmp(int a, int b)
{
    return a > b;
}
void init_hash()
{
    for (int i = 1; i <= n; i++)
        t[i] = a[i];
    sort(t + 1, t + 1 + n);
    m = unique(t + 1, t + 1 + n) - t - 1;
}
int build(int l, int r)
{
    int root = tot++;
    c[root] = 0;
    if (l != r)
    {
        int mid = (l + r) >> 1;
        lson[root] = build(l, mid);
        rson[root] = build(mid + 1, r);
    }
    return root;
}
int hash0(int x)
{
    return lower_bound(t + 1, t + 1 + m, x) - t;
}
int update(int root, int pos, int val)
{
    int newroot = tot++, tmp = newroot;
    c[newroot] = c[root] + val;
    int l = 1, r = m;
    while (l < r)
    {
        int mid = (l + r) >> 1;
        if (pos <= mid)
        {
            lson[newroot] = tot++;
            rson[newroot] = rson[root];
            newroot = lson[newroot];
            root = lson[root];
            r = mid;
        }
        else
        {
            rson[newroot] = tot++;
            lson[newroot] = lson[root];
            newroot = rson[newroot];
            root = rson[root];
            l = mid + 1;
        }
        c[newroot] = c[root] + val;
    }
    return tmp;
}
int query(int left_root, int right_root, int k)
{
    int l = 1, r = m;
    while (l < r)
    {
        int mid = (l + r) >> 1;
        if (c[lson[left_root]] - c[lson[right_root]] >= k)
        {
            r = mid;
            left_root = lson[left_root];
            right_root = lson[right_root];
        }
        else
        {
            l = mid + 1;
            k -= c[lson[left_root]] - c[lson[right_root]];
            left_root = rson[left_root];
            right_root = rson[right_root];
        }
    }
    return l;
}
int pd(int a, int b, int c)
{
    if (a*1ll < b*1ll + c)
        return 1;
    return 0;
}
int main()
{
    while (scanf("%d%d", &n, &q) != EOF)
    {
        tot = 0;
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
        }
        init_hash();
        T[n + 1] = build(1, m);
        for (int i = n; i; i--)
        {
            int pos = hash0(a[i]);
            T[i] = update(T[i + 1], pos, 1);
        }
        // int l,r,k;
        // scanf("%d%d%d",&l,&r,&k);
        // printf("%d\n",t[query(T[l],T[r+1],k)]);
        while (q--)
        {
            int l, r, k;
            scanf("%d%d", &l, &r);
            int cha = r - l + 1;
            if (cha > 2)
            {

                // printf("%d\n",t[query(T[l],T[r+1],k)]);
                int a1 = t[query(T[l], T[r + 1], cha)], a2 = t[query(T[l], T[r + 1], cha - 1)], a3 = t[query(T[l], T[r + 1], cha - 2)];
                int f = 0;
                if (pd(a1, a2, a3))
                {
                    printf("%lld\n", 1ll*a1 + a2 + a3);
                    f = 1;
                }
                else
                {

                    for (int i = cha - 3; i; --i)
                    {
                        a1 = a2;
                        a2 = a3;
                        a3 = t[query(T[l], T[r + 1], i)];
                        if (pd(a1, a2, a3))
                        {
                            // printf("%d %d %d\n", a1, a2, a3);
                            printf("%lld\n", a1 + a2 + a3);
                            f = 1;
                            break;
                        }
                    }
                }
                if (!f)
                    printf("-1\n");
            }
            else
                printf("-1\n");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值