HDU - 6601 2019 Multi-University Training Contest 2(关于斐波那契有趣的性质)

本文介绍了一种使用线段树数据结构解决区间内最大三角形周长查询问题的方法。通过维护区间前50大的边长,并利用斐波那契数列特性判断是否能构成三角形,最终实现高效查询。

Keen On Everything But Triangle

在这里插入图片描述
simple input

5 3
2 5 6 5 2
1 3
2 4
2 5

simple output

13
16
16

题目大意:
N个数,这些数是三角形的边长,Q次查询,每次查询一个区间,问查询的区间里面构成最大三角形的周长是多少,如果区间里面的边不能构成三角形,那就输出-1

分析:
1e5次的查询,大家可能会想到线段树的查询,但是线段树需要去维护谁呢,这是个问题,维护最大值吗,这样的话递推到树顶的话不一定就是最大值,那要怎么处理呢,大家看下面这个程序:

int a[100]={0,1,1,2,3};
    for(int i=5;i<=50;i++){
        a[i]=a[i-1]+a[i-2];
        cout<<i<<" "<<a[i]<<endl;
    }

在这里插入图片描述
没错,这就是斐波那契数列,因为题目中说明了变成是1e9之内的,也就是说都是在int范围内的,那么就可以说明,如果边长的个数超过了47的话,就是一定能构成三角形的,因为菲波那切数列是刚好后一项是前两项的和,那么这样的话就可以给出我们一个思路了:
线段树只需要维护一个区间的前50个大的边就好了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<cmath>
#include<cstring>
#include<set>
#include<queue>
#include<stack>
#include<map>
typedef long long ll;
using namespace std;
const int N=4e5+100;
struct node{
    int l,r,cnt;
    ll maxv[55];//区间的前50个大的变边
}num[N];
ll a[510],res[510];
int tot;
void push_up(int x){
    int i=0,j=0;
    for(int k=0;k<50;k++){

        if(num[x<<1].maxv[i]>num[x<<1|1].maxv[j])
            num[x].maxv[k]=num[x<<1].maxv[i],i++;
        else
            num[x].maxv[k]=num[x<<1|1].maxv[j],j++;
    }
}
void build(int id,int l,int r){
    num[id].l=l,num[id].r=r;
    if(l==r){
        for(int i=0;i<50;i++)
        num[id].maxv[i]=0;

        scanf("%lld",&num[id].maxv[0]);
        num[id].cnt=1;
        return ;
    }
    int mid=(l+r)/2;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    push_up(id);
}

void query(int id,int l,int r){

    if(l==num[id].l&&num[id].r==r){

    for(int i=0;i<50;i++)
        res[i]=a[i];
    int i=0,j=0;
    for(int k=0;k<50;k++){
        if(num[id].maxv[i]>res[j])
            a[k]=num[id].maxv[i],i++;
        else
            a[k]=res[j],j++;
      }
        return;
    }
    int mid=(num[id].l+num[id].r)/2;
    if(mid>=r) query(id<<1,l,r) ;
    else if(mid<l) query(id<<1|1,l,r);
    else{
        query(id<<1,l,mid);
        query(id<<1|1,mid+1,r);
    }
}
ll solve(int l,int r){
    query(1,l,r);
    sort(a,a+50,greater<ll>());

    for(int i=0;i<48;i++)
        if(a[i]<a[i+1]+a[i+2])
        return a[i]+a[i+1]+a[i+2];
    return -1;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    int n,q;
    while(scanf("%d%d",&n,&q)!=EOF){
        build(1,1,n);
        int l,r;
        for(int i=0;i<q;i++){
        scanf("%d%d",&l,&r);
        for(int i=0;i<300;i++) a[i]=0;

        printf("%lld\n",solve(l,r));
        }
    }
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值