Keen On Everything But Triangle
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 4879 Accepted Submission(s): 1182
Problem Description
N sticks are arranged in a row, and their lengths are a1,a2,...,aN.
There are Q querys. For i-th of them, you can only use sticks between li-th to ri-th. Please output the maximum circumference of all the triangles that you can make with these sticks, or print −1 denoting no triangles you can make.
Input
There are multiple test cases.
Each case starts with a line containing two positive integers N,Q(N,Q≤105).
The second line contains N integers, the i-th integer ai(1≤ai≤109) of them showing the length of the i-th stick.
Then follow Q lines. i-th of them contains two integers li,ri(1≤li≤ri≤N), meaning that you can only use sticks between li-th to ri-th.
It is guaranteed that the sum of Ns and the sum of Qs in all test cases are both no larger than 4×105.
Output
For each test case, output Q lines, each containing an integer denoting the maximum circumference.
Sample Input
5 3 2 5 6 5 2 1 3 2 4 2 5
Sample Output
13 16 16
Source
2019 Multi-University Training Contest 2
Recommend
liuyiding | We have carefully selected several similar problems for you: 6730 6729 6728 6727 6726
OJ题号
简单题意
题意:n 根木棍,若干次询问 [l,r] 区间内的木棍能组成的三角形的最大周长。
正解思路
首先,我们需要知道一个定理
这题的最大数为1e9,因为第44斐波那契数列已经查过了,所以我们只需要选出前44k的数就可以了(裸主席树)。然后贪心的考虑区间最大的三个数能否形成三角形,如果不能,考虑区间第 二大、第三大、第四大的三个数,以此类推,直到能形成三角形。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=1e5+7;
const int mod=1e9+7;
int t,n,m,cnt,root[maxn];
LL a[maxn],b[maxn];
//cnt和root:主席树的总点数和每一个根
struct node
{
int l,r,sum;
} T[maxn*40];
vector<int> v;
int getid(int x) //离散化
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
/*
v.clear();
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
*/
//清空,默认给root[0]
int build(int l,int r)
{
int cur=++cnt;
T[cur].sum=0;
if(l==r)
return cur;
int mid=l+r>>1;
build(l,mid);
build(mid+1,r);
return cur;
}
///单点修改
void update(int l,int r,int &now,int pre,int pos,int add)
{
T[++cnt]=T[pre];
T[cnt].sum+=add;
now=cnt;
if(l==r)
return;
int mid=(l+r)>>1;
if(pos<=mid)
update(l,mid,T[cnt].l,T[pre].l,pos,add);
else
update(mid+1,r,T[cnt].r,T[pre].r,pos,add);
}
///查询区间[x,y]第k小,使用v[query(1,n,root[x-1],root[y],mid)-1]
LL query(int l,int r,int x,int y,int k)
{
if(l==r)
return l;
int mid=(l+r)/2;
int sum=T[T[y].l].sum-T[T[x].l].sum;
if(sum>=k)
return query(l,mid,T[x].l,T[y].l,k);
else
return query(mid+1,r,T[x].r,T[y].r,k-sum);
}
/*
///查询区间[x,y]<=k的元素个数 query_min(root[x-1],root[y],1,n,k)
int two_find(int x,int y,int k)///二分查找第i小的元素小于等于k的最大元素
//即求k是第几小的元素
{
int l=0,r=y-x+1;
while(l<r)
{
int mid=(l+r+1)>>1;
if(v[query(1,n,root[x-1],root[y],mid)-1]<=k)
l=mid;
else
r=mid-1;
}
return l;
}
///查询区间[x,y]>=k的最小值 query_min(root[x-1],root[y],1,n,k)
int query_min(int lrot,int rrot,int l,int r,int k)
{
if(l==r)
{
if(T[rrot].sum-T[lrot].sum>0)
return l;
else
return 1e9;
}
int mid=(l+r)>>1;
if(k<=mid)
{
int ans=1e9;
if(k<=l)//这里相当于一个剪枝,在小于l的时候,如果左子树有符合条件的就进入左子树,否则再进入右子树。
{
if(T[T[rrot].l].sum-T[T[lrot].l].sum>0)
ans=min(ans,query_min(T[lrot].l,T[rrot].l,l,mid,k));
else if(T[T[rrot].r].sum-T[T[lrot].r].sum>0)
ans=min(ans,query_min(T[lrot].r,T[rrot].r,mid+1,r,k));
return ans;
}
if(T[T[rrot].l].sum-T[T[lrot].l].sum>0) //k在l到mid之间的时候,左右子树都有可能涉及,就左右都看一遍寻找最优解
ans=min(ans,query_min(T[lrot].l,T[rrot].l,l,mid,k));
if(T[T[rrot].r].sum-T[T[lrot].r].sum>0)
ans=min(ans,query_min(T[lrot].r,T[rrot].r,mid+1,r,k));
return ans;
}
else
{
int ans=1e9;//k大于mid的时候,直接进入右子树,左子树不用找了
if(T[T[rrot].r].sum-T[T[lrot].r].sum>0)
ans=min(ans,query_min(T[lrot].r,T[rrot].r,mid+1,r,k));
return ans;
}
}
///查询区间[x,y]的种类个数query_num(1,n,root[y],x)
int query_num(int l,int r,int root,int left)
{
if(l>=left)
return T[root].sum;
int mid=(l+r)>>1;
if(mid>=left)
return query_num(l,mid,T[root].l,left)+T[T[root].r].sum;
else
return query_num(mid+1,r,T[root].r,left);
} */
int main()
{
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+n+1);
int b_n=unique(b+1,b+n+1)-(b+1);
for(int i=1; i<=n; i++)
{
a[i]=lower_bound(b+1,b+b_n+1,a[i])-b;
}
cnt=0;
root[0]=build(1,b_n);
for(int i=1; i<=n; i++)
{
update(1,b_n,root[i],root[i-1],a[i],1);
}
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
int num=r-l+1;
if(num < 3)
{
printf("-1\n");
continue;
}
int k=0;
LL ans=-1;
for(int i=num; i>=3; i--)
{
LL z=b[query(1,b_n,root[l-1],root[r],i)];
LL y=b[query(1,b_n,root[l-1],root[r],i-1)];
LL x=b[query(1,b_n,root[l-1],root[r],i-2)];
if(y+x>z)
{
ans=x+y+z;
break;
}
}
printf("%lld\n",ans);
}
}
return 0 ;
}
一个比较慢的做法
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=1e5+7;
const int mod=1e9+7;
int t,n,m,cnt,root[maxn];
LL a[maxn],b[maxn];
//cnt和root:主席树的总点数和每一个根
struct node
{
int l,r,sum;
} T[maxn*40];
vector<int> v;
int getid(int x) //离散化
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
/*
v.clear();
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
*/
//清空,默认给root[0]
int build(int l,int r)
{
int cur=++cnt;
T[cur].sum=0;
if(l==r)
return cur;
int mid=l+r>>1;
build(l,mid);
build(mid+1,r);
return cur;
}
///单点修改
void update(int l,int r,int &now,int pre,int pos,int add)
{
T[++cnt]=T[pre];
T[cnt].sum+=add;
now=cnt;
if(l==r)
return;
int mid=(l+r)>>1;
if(pos<=mid)
update(l,mid,T[cnt].l,T[pre].l,pos,add);
else
update(mid+1,r,T[cnt].r,T[pre].r,pos,add);
}
///查询区间[x,y]第k小,使用v[query(1,n,root[x-1],root[y],mid)-1]
LL query(int l,int r,int x,int y,int k)
{
if(l==r)
return l;
int mid=(l+r)/2;
int sum=T[T[y].l].sum-T[T[x].l].sum;
if(sum>=k)
return query(l,mid,T[x].l,T[y].l,k);
else
return query(mid+1,r,T[x].r,T[y].r,k-sum);
}
/*
///查询区间[x,y]<=k的元素个数 query_min(root[x-1],root[y],1,n,k)
int two_find(int x,int y,int k)///二分查找第i小的元素小于等于k的最大元素
//即求k是第几小的元素
{
int l=0,r=y-x+1;
while(l<r)
{
int mid=(l+r+1)>>1;
if(v[query(1,n,root[x-1],root[y],mid)-1]<=k)
l=mid;
else
r=mid-1;
}
return l;
}
///查询区间[x,y]>=k的最小值 query_min(root[x-1],root[y],1,n,k)
int query_min(int lrot,int rrot,int l,int r,int k)
{
if(l==r)
{
if(T[rrot].sum-T[lrot].sum>0)
return l;
else
return 1e9;
}
int mid=(l+r)>>1;
if(k<=mid)
{
int ans=1e9;
if(k<=l)//这里相当于一个剪枝,在小于l的时候,如果左子树有符合条件的就进入左子树,否则再进入右子树。
{
if(T[T[rrot].l].sum-T[T[lrot].l].sum>0)
ans=min(ans,query_min(T[lrot].l,T[rrot].l,l,mid,k));
else if(T[T[rrot].r].sum-T[T[lrot].r].sum>0)
ans=min(ans,query_min(T[lrot].r,T[rrot].r,mid+1,r,k));
return ans;
}
if(T[T[rrot].l].sum-T[T[lrot].l].sum>0) //k在l到mid之间的时候,左右子树都有可能涉及,就左右都看一遍寻找最优解
ans=min(ans,query_min(T[lrot].l,T[rrot].l,l,mid,k));
if(T[T[rrot].r].sum-T[T[lrot].r].sum>0)
ans=min(ans,query_min(T[lrot].r,T[rrot].r,mid+1,r,k));
return ans;
}
else
{
int ans=1e9;//k大于mid的时候,直接进入右子树,左子树不用找了
if(T[T[rrot].r].sum-T[T[lrot].r].sum>0)
ans=min(ans,query_min(T[lrot].r,T[rrot].r,mid+1,r,k));
return ans;
}
}
///查询区间[x,y]的种类个数query_num(1,n,root[y],x)
int query_num(int l,int r,int root,int left)
{
if(l>=left)
return T[root].sum;
int mid=(l+r)>>1;
if(mid>=left)
return query_num(l,mid,T[root].l,left)+T[T[root].r].sum;
else
return query_num(mid+1,r,T[root].r,left);
} */
LL res[maxn];
int main()
{
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+n+1);
int b_n=unique(b+1,b+n+1)-(b+1);
for(int i=1; i<=n; i++)
{
a[i]=lower_bound(b+1,b+b_n+1,a[i])-b;
}
cnt=0;
root[0]=build(1,b_n);
for(int i=1; i<=n; i++)
{
update(1,b_n,root[i],root[i-1],a[i],1);
}
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
int num=r-l+1;
if(num < 3)
{
printf("-1\n");
continue;
}
int k=0;
for(int i=1; i<=50; i++)
{
int j=num-i+1;
if(j<=0) break;
res[++k]=b[query(1,b_n,root[l-1],root[r],j)];
}
LL ans=-1;
for(int i=1; i<=k-2; i++)
{
if(res[i]<res[i+1]+res[i+2])
{
ans=res[i]+res[i+1]+res[i+2];
break;
}
}
printf("%lld\n",ans);
}
}
return 0 ;
}