2019杭电多校第二场 K - Keen On Everything But Triangle (主席树)

题目链接:HDU - 6601

题意:多组输入,n个数字,q次询问,每次询问区间[L,R]内构成的最大三角形周长是多少,无法构成的话为-1。

 

如果三个数字无法构成三角形,那是不是某一条边大于等于另外两条边的和。

那考虑等于,在int范围内是不是最坏的情况是一个斐波那契数列,这个序列长度为44。

所以如果一个区间长度大于44的话,我一定可以在44次询问中找到答案,所以我们用主席树维护,每次询问区间第k小,k-1小,k-2小,直到k<=2,看这之间是否出现答案。

否则的话小于等于44,那也是直接跑过去就可以。

复杂度n*logn*44。

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+7;
typedef long long ll;

int b[maxn];
int a[maxn];
int m;

void quchong(int n){
    sort(b+1,b+1+n);
    m=unique(b+1,b+1+n)-b-1;
}

int getid(int x){
    return lower_bound(b+1,b+1+m,x)-b;
}

int root[maxn];
struct Tree{
    int lc,rc;
    int sum;
}tree[4322089];

int tot;

int build(int l,int r){
    int k=++tot;
    tree[k].sum=0;
    if(l==r){
        return k;
    }
    int mid=(l+r)>>1;
    tree[k].lc=build(l,mid);
    tree[k].rc=build(mid+1,r);
    return k;
}

int updata(int now,int l,int r,int id,int val){
    int k=++tot;
    tree[k]=tree[now];
    if(l==r){
        ++tree[k].sum;
        return k;
    }
    int mid=(l+r)>>1;
    if(id<=mid) tree[k].lc=updata(tree[now].lc,l,mid,id,val);
    else tree[k].rc=updata(tree[now].rc,mid+1,r,id,val);
    tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;
    return k;
}

int myfind(int L,int R,int l,int r,int h){
    if(l==r) return l;
    int val=tree[tree[R].lc].sum-tree[tree[L].lc].sum;
    int mid=(l+r)>>1;
    if(h<=val) return myfind(tree[L].lc,tree[R].lc,l,mid,h);
    return myfind(tree[L].rc,tree[R].rc,mid+1,r,h-val);
}

bool check(int id1,int id2,int id3){
    int x=b[id1],y=b[id2],z=b[id3];
    if(x+1LL*y>z&&x-y<z) return 1;
    return 0;
}

int main(){
    int t;
    int n,q;

    while(scanf("%d%d",&n,&q)!=EOF){

        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        quchong(n);
        tot=0;
        root[0]=build(1,m);
        for(int i=1;i<=n;++i)
            root[i]=updata(root[i-1],1,m,getid(a[i]),1);
        int l,r;
        ll res;
        while(q--){
            res=-1;
            scanf("%d%d",&l,&r);
            int k=r-l+1;
            while(k>2){//放心跑就可以。
                int id1=myfind(root[l-1],root[r],1,m,k);
                int id2=myfind(root[l-1],root[r],1,m,k-1);
                int id3=myfind(root[l-1],root[r],1,m,k-2);
                --k;
                if(check(id1,id2,id3)){
                    res=b[id1]+1LL*b[id2]+1LL*b[id3];
                    break;
                }
            }

            printf("%lld\n",res);
        }
    }

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值