hdu - 6601 Keen On Everything But Triangle 主席树求第k大

解决HDU 6601题目,使用主席树查询区间第k大元素,确保在给定区间内找到三个数构成三角形,并计算最大周长。复杂度为n*logn*50。

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6601

 

题意:

       给你n个数字和q个询问,每次询问给你一个区间l, r 。问你在这个区间中能否找到三个数能组成三角形,如果能,请给出周长最大的三角形的周长,如果不能则输出-1。

做法:

        啊啊啊想到死,真是懊恼明明离想的很近了来着。如果不能形成三角形,最坏情况下是形成了斐波那契数列,如1 1 2 3 5 ,使得a<=b<=c的时候形成了a+b=c,可斐波那契数列在第45项就已经超过了1e9,所以只要找到区间前45个大的数字就至少有三个数字可以形成三角形,假设a<=b<=c<=d,那如果a+b>d的话那b+c>d也是一定的,所以能形成最大三角形的三个数一定的连续的,然后就是主席树求区间第k大模板了。。只要for不超过50次就好了,复杂度是n*logn*50

 


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int root[maxn],n,q;
int cnt,a[maxn];
vector<int> v;
struct node{
    int lson,rson,num;
}e[maxn*40];
int gain(int x){
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void update(int l,int r,int &now,int pre,int a){
    now=++cnt;
    e[now]=e[pre];
    e[now].num++;
    if(l==r) return ;
    int mid=(l+r)/2;
    if(a<=mid) update(l,mid,e[now].lson,e[pre].lson,a);
    else  update(mid+1,r,e[now].rson,e[pre].rson,a);
}
int query(int l,int r,int now,int pre,int k){
    if(l==r) return l;
    int mid=(l+r)/2;
    int ti=e[e[now].lson].num-e[e[pre].lson].num;
    if(ti>=k) return query(l,mid,e[now].lson,e[pre].lson,k);
    else return query(mid+1,r,e[now].rson,e[pre].rson,k-ti);
}

int main(){

    while(~scanf("%d%d",&n,&q)){
        cnt=0; memset(root,0,sizeof(root));
        e[cnt].lson=e[cnt].rson=e[cnt].num=0;
        v.clear();
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            v.push_back(a[i]);
        }
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());
        for(int i=1;i<=n;i++){
            update(1,n,root[i],root[i-1],gain(a[i]));
        }
        while(q--){
            int l,r,k;
            scanf("%d%d",&l,&r);
            ll ans=-1;
            int num=r-l+1;
            for(int i=1;i<=min(50,num-2);i++){
                ll fi=(ll)v[query(1,n,root[r],root[l-1],num-i+1)-1];
                //printf("di %d da is %lld\n",num-i+1,fi);
                ll se=(ll)v[query(1,n,root[r],root[l-1],num-i)-1];
                ll th=(ll)v[query(1,n,root[r],root[l-1],num-i-1)-1];
                if(se+th>fi){
                    //printf("fi = %lld se = %lld th = %lld\n",fi,se,th);
                    ans=fi+se+th;
                    break;
                }
            }
            printf("%lld\n",ans);
        }
    }

    return 0;
}
/*
10 8
1 1 2 3 5 7 7 9 5 6
1 3
1 5
7 10
6 9
6 8
9 10


1000000000

*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值