版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
题目链接
题意:给你n个数,q次询问,每次询问一个区间 [l, r],任选三边组成三角形,边只能单次使用,求最大周长。
题解:算最大周长的思路是将 [l, r]的边进行排序,从大往小取边,如果不能构造出三角形就舍弃最长边,继续判断。
但是排序加上q次询问肯定会T,因此我们可以用主席树维护,可以通过查询第k大取边,时间复杂的是logn,慢慢枚举。
斐波那契数列是极端不能形成三角形的序列,所以最多47次就可以查询出结果。
AC_code:
//
//Write by Yuan Xilan on 2019...
//
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const int maxn = 1e5 + 5;
int N, Q, ans;
vector < int >v;
int a[maxn], root[maxn * 40];
struct node {
int l, r, sum; //sum表示以该结点为根的子树所含数组内元素的个数。
} t[maxn * 40];
int get_id(int x)
{
return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}
void update(int l, int r, int &x, int y, int pos)
{
t[++ans] = t[y];
t[ans].sum += 1;
x = ans;
if (l == r)
return;
int mid = (l + r) >> 1;
if (pos <= mid)
update(l, mid, t[x].l, t[y].l, pos);
else
update(mid + 1, r, t[x].r, t[y].r, pos);
}
int query(int l, int r, int x, int y, int k)
{
if (l == r)
return l;
int mid = (l + r) >> 1;
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);
}
void solve()
{
while (~scanf("%d%d", &N, &Q))
{
if (N == -1)
break;
v.clear();
ans = 0;
for (int i = 1; i <= N; ++i)
{
scanf("%d", &a[i]);
v.push_back(a[i]);
}
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
for (int i = 1; i <= N; ++i)
{
update(1, N, root[i], root[i - 1], get_id(a[i]));
}
int b[5];
while (Q--)
{
int times = 0;
bool flag = true;
int x, y;
scanf("%d%d", &x, &y);
int k = y - x + 1; //表示第k大
for (; k >= 1; --k)
{
int tmp =
v[query(1, N, root[x - 1], root[y], k) - 1];
b[++times] = tmp;
if (times == 3)
{
if (b[2] + b[3] > b[1])
{
printf("%lld\n",(ll) b[1] + b[2] + b[3]);//注意这里不用longlong会炸
flag = false;
break;
} else
{
b[1] = b[2];
b[2] = b[3];
times -= 1;
}
}
}
if (flag)
puts("-1");
}
}
}
int main()
{
solve();
return 0;
}