HDU6601 Keen On Everything But Triangle 线段树或主席树

老子叫甜甜 提交于 2020-02-01 10:00:34

网址:https://vjudge.net/problem/HDU-6601

题意:

给出序列$a_1,a_2,a_3,......,a_n$代表棍子的长度,和$Q$次询问,对于第$i$次询问,在$l_i$和$r_i$的区间中选3根棍子构成三角形,输出三角形最大周长,如果组成不了或者根子不够,输出$-1$。

题解:

一、主席树做法:

直接对于每个区间查询第$1$,第$2$,$......$,一直到第$r-l+1$大,一旦发现成功的三角形则输出,如果都不行或者根子不够输出$-1$。

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN=100005;
struct chieftree
{
    struct node
    {
        int l,r,sum;
    };
    node tr[MAXN*20];
    int rt[MAXN];
    int cnt;
    void init()
    {
        cnt=0;
    }
    void build(int &rt,int l,int r)
    {
        rt=++cnt;
        tr[rt].sum=0;
        if(l==r)
            return;
        int m=(l+r)/2;
        build(tr[rt].l,l,m);
        build(tr[rt].r,m+1,r);
    }
    void update(int &rt,int l,int r,int val)
    {
        tr[++cnt]=tr[rt];
        rt=cnt;
        ++tr[rt].sum;
        if(l==r)
            return;
        int m=(l+r)/2;
        if(val<=m)
            update(tr[rt].l,l,m,val);
        else
            update(tr[rt].r,m+1,r,val);
    }
    int query(int rl,int rr,int l,int r,int val)
    {
        int d=tr[tr[rr].r].sum-tr[tr[rl].r].sum;
        if(l==r)
            return l;
        int m=(l+r)/2;
        if(val<=d)
            return query(tr[rl].r,tr[rr].r,m+1,r,val);
        else   
            return query(tr[rl].l,tr[rr].l,l,m,val-d);
    }
};
chieftree tr;
void print()
{
    for(int i=1;i<=tr.cnt;++i)
        cout<<tr.tr[i].l<<" "<<tr.tr[i].r<<" "<<tr.tr[i].sum<<endl;
}
long long a[MAXN],b[MAXN];
long long ans(int l,int r,int n)
{
    if(r-l+1<3)
        return -1;
    int pa=tr.query(tr.rt[l-1],tr.rt[r],1,n,1),pb=tr.query(tr.rt[l-1],tr.rt[r],1,n,2),pc,k=2;
    do{
        pc=tr.query(tr.rt[l-1],tr.rt[r],1,n,++k);
        //cout<<b[pa]<<" "<<b[pb]<<" "<<b[pc]<<endl;
        if(b[pa]<b[pb]+b[pc])
            return b[pa]+b[pb]+b[pc];
        pa=pb;
        pb=pc;
    }while(k<r-l+1);
    return -1;
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=n;++i)
        {
            scanf("%lld",&a[i]);
            b[i]=a[i];
        }
        sort(b+1,b+1+n);
        int nnew=unique(b+1,b+1+n)-b-1;
        tr.init();
        tr.build(tr.rt[0],1,nnew);
        for(int i=1;i<=n;++i)
        {
            tr.rt[i]=tr.rt[i-1];
            int pos=lower_bound(b+1,b+1+nnew,a[i])-b;
            //cout<<pos<<endl;
            tr.update(tr.rt[i],1,nnew,pos);
        }
        //print();
        int aa,bb;
        for(int i=0;i<m;++i)
        {
            scanf("%d%d",&aa,&bb);
            printf("%lld\n",ans(aa,bb,nnew));
        }
    }
    return 0;
}

 

 二、线段树做法:

考虑到最坏情况,这个区间形成斐波那契数列,此时,到第$45$位时已经超出$1e9$,所以我们只要在线段树中保存前$50$大,然后查询时记录查询到的区间,再在这些区间中暴力找到前$44$大即可(vector一定要先确定容量再填充元素,不能push_back,否则会TLE)。

AC代码:

#include <bits/stdc++.h>
//#pragma GCC optimize(2)
using namespace std;
#define MAXN 100005
int num[MAXN];
void print(vector<int> &vec)
{
    for(auto &i:vec)
        cout<<" "<<i;
    cout<<endl;
}
int tagtop;
struct SegTree
{
    struct node
    {
        vector<int>val;
        int l,r;
    };
    node tr[MAXN<<2];
    void build(int l,int r,int k)
    {
        tr[k].l=l,tr[k].r=r;
        if(l==r)
        {
            tr[k].val.resize(1);
            tr[k].val[0]=num[l];
            return;
        }
        int m=(l+r)/2;
        build(l,m,k<<1);
        build(m+1,r,(k<<1)+1);
        tr[k].val.resize(min(90,r-l+1));
        merge(tr[k<<1].val.begin(),tr[k<<1].val.end(),tr[(k<<1)+1].val.begin(),tr[(k<<1)+1].val.end(),
            tr[k].val.begin(),greater<int>());
        tr[k].val.resize(min(45,r-l+1));
    }
    int tag[MAXN],tagpos[MAXN];
    void query(int l,int r,int k)
    {
        if(l<=tr[k].l&&r>=tr[k].r)
        {
            tag[tagtop]=k,tagpos[tagtop++]=0;//tag标记区间,tagpos标记此时在该区间中查询到的值的位置
            return;
        }
        int m=(tr[k].l+tr[k].r)/2;;
        if(l<=m)
            query(l,r,k<<1);
        if(r>m)
            query(l,r,(k<<1)+1);
    }
    int val[50],maxtop,maxval,maxpos;
    long long answer(int l,int r)
    {
        if(r-l+1<3)
            return -1;
        query(l,r,1);
        bool flag=0;
        int t=45;
        maxtop=0;
        while(t--&&!flag)
        {
            maxval=maxpos=0;
            flag=1;
            for(int i=0;i<tagtop;++i)//找最大值,对于每个已经记录的区间
            {
                if(tagpos[i]<tr[tag[i]].val.size())//没有选完
                {
                    if(tr[tag[i]].val[tagpos[i]]>maxval)//记录最大值和所属区间
                    {
                        maxval=tr[tag[i]].val[tagpos[i]];
                        maxpos=i;
                    }
                    flag=0;
                }
            }
            if(!flag)
            {
                ++tagpos[maxpos];//被选取了最大值的区间的选择标记后移一位
                val[maxtop++]=maxval;//记录这个最大值
            }
        }
        for(int i=2;i<maxtop;++i)//组合三角形
            if(val[i-2]<val[i-1]+val[i])
                return (long long)(val[i-2])+(long long)(val[i-1])+(long long)(val[i]);
        return -1;
    }
};
SegTree tr;
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;++i)
            scanf("%d",&num[i]);
        tr.build(1,n,1);
        int l,r;
        for(int i=0;i<m;++i)
        {
            scanf("%d%d",&l,&r);
            tagtop=0;
            printf("%lld\n",tr.answer(l,r));
        }
    }
    return 0;
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!