1.牛客多校的题目,大意就是:找出有多少个区间满足:区间最大值的两倍小于等于区间和。统计这样的区间数并输出答案。
2.我们可以枚举最大值,对于每个a[i],他都可以作为某些区间的最大值,可以找出这段区间最左边的端点和最右边的端点。然后枚举一个二分另外一个即可。至于怎么枚举最值,可以分治,分治的时候会出现一些比较强的数据,让你的分治失效,如果你每次都是默认的枚举左端点二分右端点,那么可能区间不是很理想,我们肯定是要二分大的区间,枚举小的区间,所以在分治的过程中,有选择的枚举即可。这也叫做启发式分治。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 3e5 + 10;
int a[maxn], n, dp[maxn][25], pos[maxn][25];
long long pre[maxn], suf[maxn];
void init()
{
for (int i = 1; i <= n; i++) dp[i][0] = a[i], pos[i][0] = i;
for (int j = 1; (1 << j) <= n; j++) for (int i = 1; i + (1 << j) - 1 <= n; i++) {
if (dp[i][j - 1] >= dp[i + (1 << (j - 1))][j - 1]) dp[i][j] = dp[i][j - 1], pos[i][j] = pos[i][j - 1];
else dp[i][j] = dp[i + (1 << (j - 1))][j - 1], pos[i][j] = pos[i + (1 << (j - 1))][j - 1];
}
}
int query(int l, int r)
{
int k = 0;
while (l + (1 << (k + 1)) - 1 <= r) k++;
if (dp[l][k] >= dp[r - (1 << k) + 1][k]) return pos[l][k];
return pos[r - (1 << k) + 1][k];
}
ll dfs(int l, int r)
{
if (l >= r) return 0;
int max_pos = query(l, r);
ll res = 0;
if (max_pos - l < r - max_pos)
for (int i = l; i <= max_pos; i++)
res += r - (lower_bound(pre + max_pos, pre + r + 1, pre[i - 1] + 2 * a[max_pos]) - pre - 1);
else
for (int i = max_pos; i <= r; i++)
res += (n - l + 1) - (lower_bound(suf + n - max_pos + 1, suf + n - l + 2, suf[n - i] + 2 * a[max_pos]) - suf - 1);
return res + dfs(l, max_pos - 1) + dfs(max_pos + 1, r);
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) pre[i] = pre[i - 1] + a[i], suf[i] = suf[i - 1] + a[n - i + 1];
init();
printf("%lld\n", dfs(1, n));
}
}