2019 Multi-University Training Contest 2 补题

本文介绍了一道算法题目,通过线段树维护区间内的最大值来找出能构成的最大三角形周长。讨论了实现细节及两种不同实现方式的时间效率。

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

1011:http://acm.hdu.edu.cn/showproblem.php?pid=6601

题意:给你n个数,q次询问,每次询问一个区间[l,r]表示只能从[l,r]区间里选最大的能构成三角形的三条边,输出其最大周长。

分析:一看肯定说用线段树维护,但。。。。。比赛时想了好久不知道维护什么,甚至还不顾莫队的T飞的复杂度交了几发。

维护一个区间的45个最大值,这样一定能找到周长最大的三角形,因为斐波拉契数列f[45]>1e9,如果45个最大值都试完了还找不到三角形则肯定不存在。最坏的情况也就是区间的数刚好是斐波拉契数列。

然后再线段树区间合并就行了,合并时采用有序表的合并方法,复杂度O(45+45),同时还可以加上一些优化,比如如果已经找到了满足条件的最大三条边就不用再往下找较小的满足条件的边了,因为它们已经没用了(就算那些较小的边可以和其他子树的边合并也不会大于刚刚找的满足条件的最大三条边)。

Ac code:(查询未优化 2200ms)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+1;
int a[maxn];
struct Tree{
    int d[46];
    int k;
    Tree(int k=0):k(k){}
    inline Tree Merge(const Tree& a,const Tree& b)
    {
        Tree res;
        int i=1,j=1;
        while(i<=a.k&&j<=b.k)
        {
            if(res.k>=45) return res;
            if(a.d[i]<=b.d[j]){
                res.d[++res.k]=b.d[j];
                j++;
            }
            else{
                res.d[++res.k]=a.d[i];
                i++;
            }
            if(res.k>=3&&res.d[res.k]+res.d[res.k-1]>res.d[res.k-2]) return res;
        }
        while(i<=a.k){
            if(res.k>=45) return res;
            res.d[++res.k]=a.d[i++];
            if(res.k>=3&&res.d[res.k]+res.d[res.k-1]>res.d[res.k-2]) return res;
        }
        while(j<=b.k){
            if(res.k>=45) return res;
            res.d[++res.k]=b.d[j++];
            if(res.k>=3&&res.d[res.k]+res.d[res.k-1]>res.d[res.k-2]) return res;
        }
        return res;
    }
}tree[maxn<<2];
void pushup(int rt)
{
    tree[rt]=tree[rt].Merge(tree[rt<<1],tree[rt<<1|1]);
}
void buildtree(int rt,int l,int r)
{
    if(l==r){
        tree[rt].d[1]=a[l];
        tree[rt].k=1;
        return;
    }
    int mid=(l+r)>>1;
    buildtree(rt<<1,l,mid);
    buildtree(rt<<1|1,mid+1,r);
    pushup(rt);
}
Tree query(int rt,int l,int r,int L,int R)
{
    if(L<=l&&r<=R){
        return tree[rt];
    }
    Tree ans;
    int mid=(l+r)>>1;
    if(mid>=L) ans=ans.Merge(ans,query(rt<<1,l,mid,L,R));
    if(mid<R) ans=ans.Merge(ans,query(rt<<1|1,mid+1,r,L,R));
    return ans;
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        buildtree(1,1,n);
        int l,r;
        while(m--){
            scanf("%d%d",&l,&r);
            if(r-l+1<3){
                puts("-1");
                continue;
            }
            Tree cnt=query(1,1,n,l,r);
            ll ans=-1;
            for(int i=1;i<=cnt.k-2;i++)
            {
                if(cnt.d[i+1]+cnt.d[i+2]>cnt.d[i]){
                    ans=1ll*cnt.d[i+1]+1ll*cnt.d[i+2]+1ll*cnt.d[i];
                    break;
                }
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}

 

查询优化 920ms:

///920ms
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+1;
int a[maxn];
struct Tree
{
    int d[46];
    int k;
    void init()
    {
        k=0;
        for(int i=1; i<=45; i++) d[i]=0;
    }
    inline Tree Merge(const Tree& a,const Tree& b)
    {
        Tree res;
        res.k=0;
        int i=1,j=1;
        while(i<=a.k&&j<=b.k)
        {
            if(res.k>=45) return res;
            if(a.d[i]<=b.d[j])
            {
                res.d[++res.k]=b.d[j];
                j++;
            }
            else
            {
                res.d[++res.k]=a.d[i];
                i++;
            }
            if(res.k>=3&&res.d[res.k]+res.d[res.k-1]>res.d[res.k-2]) return res;
        }
        while(i<=a.k)
        {
            if(res.k>=45) return res;
            res.d[++res.k]=a.d[i++];
            if(res.k>=3&&res.d[res.k]+res.d[res.k-1]>res.d[res.k-2]) return res;
        }
        while(j<=b.k)
        {
            if(res.k>=45) return res;
            res.d[++res.k]=b.d[j++];
            if(res.k>=3&&res.d[res.k]+res.d[res.k-1]>res.d[res.k-2]) return res;
        }
        return res;
    }
} tree[maxn<<2];
void pushup(int rt)
{
    tree[rt]=tree[rt].Merge(tree[rt<<1],tree[rt<<1|1]);
}
void buildtree(int rt,int l,int r)
{
    if(l==r)
    {
        tree[rt].d[1]=a[l];
        tree[rt].k=1;
        return;
    }
    int mid=(l+r)>>1;
    buildtree(rt<<1,l,mid);
    buildtree(rt<<1|1,mid+1,r);
    pushup(rt);
}
ll ans[46],tmp[46];
int len;
void query(int rt,int l,int r,int L,int R)
{
    if(L<=l&&r<=R)
    {
        int tlen=len;
        for(int i=1; i<=len; i++) tmp[i]=ans[i];
        int i=1,j=1;
        len=0;
        while(i<=tree[rt].k&&j<=tlen&&len<45)
        {
            if(tree[rt].d[i]<tmp[j])
                ans[++len]=tmp[j++];
            else
                ans[++len]=tree[rt].d[i++];
            if(len>=3&&ans[len]+ans[len-1]>ans[len-2]) return;
        }
        while(i<=tree[rt].k&&len<45)
        {
            ans[++len]=tree[rt].d[i++];
            if(len>=3&&ans[len]+ans[len-1]>ans[len-2]) return;
        }
        while(j<=tlen&&len<45)
        {
            ans[++len]=tmp[j++];
            if(len>=3&&ans[len]+ans[len-1]>ans[len-2]) return;
        }
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=L) query(rt<<1,l,mid,L,R);
    if(mid<R) query(rt<<1|1,mid+1,r,L,R);
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1; i<=n; i++) scanf("%d",&a[i]);
        buildtree(1,1,n);
        int l,r;
        while(m--)
        {
            scanf("%d%d",&l,&r);
            if(r-l+1<3)
            {
                puts("-1");
                continue;
            }
            len=0;
            query(1,1,n,l,r);
            ll res=-1;
            for(int i=1; i<=len-2; i++)
                if(ans[i+1]+ans[i+2]>ans[i])
                {
                    res=ans[i+1]+ans[i+2]+ans[i];
                    break;
                }
            printf("%lld\n",res);
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值