网址: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
",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
",tr.answer(l,r));
}
}
return 0;
}