链接:http://acm.hdu.edu.cn/showproblem.php?pid=6601、
题意:多组样例。给你一个n和q,接下来一行n个数,加下来q行,每行给出l、r,求区间[l,r]中的数,能组成三角形周长的最大值。不能组成则输出-1。
思路:首先,斐波那契数列中的任意三个数都不能组成三角形。如果若干数中,不能选出三个数组成三角形,那么他们肯定全是斐波那契数。因为斐波那契数列增长速度非常快,1e9范围内只有44个斐波那契数。又因为要找周长最长的三角形,所以我们用线段树维护区间内前50大的数即可,运气不好的话,有44个数全是斐波那契数,那么50个数中肯定能有组成三角形的数。询问时每次都找出区间内前50大的数,然后判断相邻的3个能不能组成三角形即可。
#include
#define ll long long
using namespace std;
const int N = 1e5+10;
const int M = 50;
struct node
{
int l,r,cnt,v[55];
}tree[N<<2];
int a[N],ans[55],temp[55],num,n,q;
void pushup(int cur)
{
int i=1,j=1,cnt1=tree[cur<<1].cnt,cnt2=tree[cur<<1|1].cnt;
while(tree[cur].cnt=tree[cur<<1|1].v[j])
tree[cur].v[tree[cur].cnt]=tree[cur<<1].v[i++];
else
tree[cur].v[tree[cur].cnt]=tree[cur<<1|1].v[j++];
}
while(tree[cur].cnt>1;
build(l,m,cur<<1);
build(m+1,r,cur<<1|1);
pushup(cur);
return ;
}
void query(int l,int r,int cur)
{
if(l<=tree[cur].l&&tree[cur].r<=r)
{
int i=1,j=1,in=0;
while(in=ans[j])
temp[in]=tree[cur].v[i++];
else temp[in]=ans[j++];
}
while(in=tree[cur<<1|1].l) query(l,r,cur<<1|1);
return ;
}
int main(void)
{
int l,r;
while(~scanf("%d%d",&n,&q))
{
num=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,n,1);
while(q--)
{
bool flag=1;
scanf("%d%d",&l,&r);
num=0;
query(l,r,1);
if(num<3)
{
printf("-1\n");
continue;
}
for(int i=1;i+2<=num;i++)
if(ans[i+1]+ans[i+2]>ans[i])
{
printf("%lld\n",1LL*ans[i]+ans[i+1]+ans[i+2]);
flag=0;
break;
}
if(flag) printf("-1\n");
}
}
return 0;
}
主席树思路:问出区间前min(r-l+1,50)大的数即可。
#include
#include
#include
#include
#include
using namespace std;
#define M(a, b) memset(a, b, sizeof(a))
#define lowbit(x) (x&(-x))
typedef long long ll;
const int N=1e5+10;
struct node
{
int l,r;
int val;
}tree[N * 22];
int n,q,a[N],id[N],root[N],cnt,ans[55],limit;
int build(int l,int r)
{
int cur=cnt++;
tree[cur].val=0;//cur的个数初始为0
if(l==r)
{
//叶节点的区间左右端点为0
tree[cur].l=0;
tree[cur].r=0;
return cur;
}
int mid=(r+l)>>1;
tree[cur].l=build(l,mid);//更新左端点
tree[cur].r=build(mid+1,r);//更新右端点
return cur;
}
int update(int up,int tar,int l,int r)
{
int cur=cnt++;//增加新端点,为期编号
tree[cur]=tree[up];//先用上一个树的节点信息
tree[cur].val++;
if(l==r) return cur;
int mid=(r+l)>>1;
//更新新建树的节点信息
if(tar<=mid) tree[cur].l=update(tree[up].l,tar,l,mid);
else tree[cur].r=update(tree[up].r,tar,mid+1,r);
return cur;
}
int query(int pl,int pr,int l,int r,int k)//询问区间第K大
{
if(l==r) return l;
int mid=(r+l)>>1;
//先看右子树数目是否够K个,够继续询问右子树
if(tree[tree[pr].r].val-tree[tree[pl].r].val>=k) return query(tree[pl].r,tree[pr].r,mid+1,r,k);
//否则询问左子树第k-右子树个数大
else return query(tree[pl].l,tree[pr].l,l,mid,k-(tree[tree[pr].r].val-tree[tree[pl].r].val));
}
int main()
{
while(~scanf("%d%d",&n,&q))
{
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
id[i]=a[i];
}
//离散化
sort(id+1,id+1+n);
int len=unique(id+1,id+1+n)-(id+1);
//初始化,只需要这一点,因为每次都用上一个树的信息
cnt=0;
root[0]=build(1,len);
for(int i=1;i<=n;i++)
{
int p=lower_bound(id+1,id+1+len,a[i])-id;
root[i]=update(root[i-1],p,1,len);//插入新的点
}
int l,r,k;
while(q--)
{
bool flag=1;
scanf("%d%d",&l,&r);
limit=min(r-l+1,50);
for(int k=1;k<=limit;k++)
ans[k]=id[query(root[l-1],root[r],1,len,k)];
for(int i=1;i+2<=limit;i++)
{
if(ans[i+1]+ans[i+2]>ans[i])
{
printf("%lld\n",1LL*ans[i]+ans[i+1]+ans[i+2]);
flag=0;
break;
}
}
if(flag) printf("-1\n");
}
}
return 0;
}