题解 三角形

本文详细解析了一道关于寻找三角形最大周长的编程题,通过线段树优化实现高效求解,避免了传统暴力算法的低效。文章首先介绍了暴力算法的基本思路,随后提出使用线段树进行优化,利用斐波那契数列的性质减少查找次数,最终实现了O(QlogNlogA)的时间复杂度。
摘要由CSDN通过智能技术生成

题解 三角形

题目描述

680d63fc73077b0e9e61c.png

_19b523.png

具体做法与心路历程

这道题一眼过去。。。暴力 O ( Q N l o g N ) O(QNlogN) O(QNlogN)我会!,每次询问将区间排序,然后贪心从最大的开始匹配,然后一路往下匹配!

然后就没了。考试时原本想打个莫队维护 s e t set set来卡一卡,结果忘记怎么打待修莫队了~,胡乱对后面 40 40 40 分打了个线段树,每次选出区间最大值,然后再去掉最大值,一路选下去知道出结果,最后再改回来。然后就 A C AC AC了。(莫名来的惊喜)。

具体做法

正解的确是线段树。

考虑每个 A i A_i Ai的范围是 1 1 1~ 1 0 9 10^9 109,如果区间最大的三条边不能组成三角形一定满足 a + b ≤ c a+b \leq c a+bc,实际上我们反过来考虑有这个关系,那么就是已知两条边 a , b a,b a,b,最大不能组成三角形的边 c c c满足 a + b = c a+b=c a+b=c,我们让 a , b = 1 a,b=1 a,b=1,每次把选出来的 c c c和原来的 b b b看成新的 a , b a,b a,b。那么这个递推式就成斐波那契数列了。所以如果我们每次取区间最大值,不能组成三角形则继续取,那么数值的减小速度是 ≥ \geq 斐波那契数列的增长速度的,约为 O ( l o g A ) O(logA) O(logA),所以我们每次取最大值实际上最多取大概 40 40 40次就会得到结果。

线段树的复杂度约为 O ( Q l o g N l o g A ) O(QlogNlogA) O(QlogNlogA)

C o d e \mathcal{Code} Code

考场上的暴力程序也在。懒得改了

/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月28日 星期一 16时20分37秒
*******************************/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>

using namespace std;

struct IO{
    template<typename T>
    IO & operator>>(T&res)
    {
        T q=1;char ch;
        while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
        res=(ch^48);
        while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
        res*=q;
        return *this;
    }
}cin;

const int maxn=1e5+10;
int n,q,tot;
long long a[maxn],b[maxn];

bool check(register long long a,register long long b,register long long c)
{
    return a<b+c;
}

/*{{{work*/
void work()
{
    register  int opt,x,y;
    long long ans=0;
    while(q--)
    {
        cin>>opt>>x>>y;
        if(opt==1)
        {
            a[x]=y;
        }
        else
        {
            tot=0;
            for(int i=x;i<=y;i++)
                b[++tot]=a[i];
            ans=0;
            sort(b+1,b+tot+1);
            for(int i=tot;i>=3;i--)
                if(check(b[i],b[i-1],b[i-2]))
                {
                    ans=b[i]+b[i-1]+b[i-2];
                    break;
                }
            printf("%lld\n",ans);
        }
    }
}
/*}}}*/

/*{{{线段树*/

namespace SegmentTree{

    struct Node{
        long long val;
        int loc;
        bool operator<(const Node & p) const
        {
            return val<p.val;
        }
        Node(long long a=0,int b=0):val(a),loc(b){}
    };

    Node tr[maxn*4];

    inline void update(int k)
    {
        tr[k]=max(tr[k<<1],tr[k<<1|1]);
    }

    void build(register int k,register int l,register int r)
    {
        if(l==r)
        {
            tr[k]=Node(a[l],l);
            return;
        }
        register int mid=(l+r)>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
        update(k);
    }

    inline void modify(register int k,register int l,register int r,register int pos,register long long val)
    {
        if(l==r)
        {
            tr[k].val=val;
            return;
        }
        register int mid=(l+r)>>1;
        if(pos<=mid)
            modify(k<<1,l,mid,pos,val);
        else
            modify(k<<1|1,mid+1,r,pos,val);
        update(k);
    }

    Node query(register int k,register int l,register int r,register int x,register int y)
    {
        if(l>=x && r<=y) return tr[k];
        if(l>y  ||  r<x) return Node(-1,-1);
        register int mid=(l+r)>>1;
        return max(query(k<<1,l,mid,x,y),query(k<<1|1,mid+1,r,x,y));
    }

};

/*}}}*/

int _top;
SegmentTree::Node stk[maxn];

void solve()
{
    SegmentTree::build(1,1,n);
    register int opt,x,y;
    while(q--)
    {
        cin>>opt>>x>>y;
        if(opt==1)
        {
            SegmentTree::modify(1,1,n,x,y);
        }
        else
        {
            register long long ans=0;
            register int len=y-x+1;
            _top=0;
            while(_top<=len)
            {
                stk[++_top]=SegmentTree::query(1,1,n,x,y);
                SegmentTree::modify(1,1,n,stk[_top].loc,-1);
                if(_top>=3 && check(stk[_top-2].val,stk[_top-1].val,stk[_top].val))
                {
                    ans=stk[_top].val+stk[_top-1].val+stk[_top-2].val;
                    break;
                }
            }
            for(register int i=1;i<=_top;i++)
                SegmentTree::modify(1,1,n,stk[i].loc,stk[i].val);
            printf("%lld\n",ans);
        }
    }
}

int main()
{
    //freopen("triangle.in","r",stdin);
    //freopen("triangle.out","w",stdout);
    cin>>n>>q;
    for(register int i=1;i<=n;i++)
        cin>>a[i];
    if(n<=1000)
    {
        work();
    }
    else
    {
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值