问题描述
有 \(N\) 个元团子(米团),按照大小升序排列。第 \(i\) 个元团子 \((1≤i≤N)\) 的大小是 \(A_i\)。
给定两个元团子 \(A\) 和 \(B\),它们的大小分别是 \(a\) 和 \(b\) ,你只有在 \(a\) 不超过 \(b\) 的一半时,才能通过将元团子 \(A\) 放在元团子\(B\)之上来制作一个元团子(kagamimochi)。
你被给予Q对整数。设 \((L_i, R_i)\) 是第 \(i\) 对 \((1≤i≤Q)\),对于每个 \(i\),解决以下问题:
仅使用从第 \(L_i\) 个到第 \(R_i\) 个的 \(R_i-L_i+1\) 个元团子,你能同时制作多少个元团子?
更具体地说,找出最大非负整数 \(K\),使得:
- 在从第\(L_i\)个到第\(R_i\)个的\(R_i - L_i + 1\)个元团子中,选择 \(2K\) 个元团子并形成 \(K\) 对。对于每对元团子,将一个放在另一个之上,以制造 \(K\) 个元团子(kagamimochi)。
题解
设答案为 \(k\),那么可以知道,在区间中最小 \(k\) 个和最大 \(k\) 个可以相互组合起来。设区间区间开头为 \(l\),末尾为 \(r\),那么可以这么组合 \((l, r - k + 1),(l + 1, r - k + 2),...,(l+i - 1,r-k+i)\)
我们尝试在 \([0,\frac{r - l + 1}{2}]\) 二分答案,二分 \(k\),设 \(N_i\) 为 \(i\) 可以在 \([N_i, n]\) 里面的数组合。
\(i\in[l, l + k - 1]\),我们要求 \(i\) 和 \(r - l + 1 - k + i\),那么可以得出 \(N_i\le r - l + 1 - k + i\) 移项得 \(N_i - i\le r - l + 1 - k\),用 ST 维护一下就行了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using std::cin, std::cout;#define lF(i, a, b) for (int i = a, END##i = b; i <= END##i; i++)
#define rF(i, a, b) for (int i = a, END##i = b; i >= END##i; i--)void Init();
void Solve();signed main() {cin.sync_with_stdio(0);cin.tie(0), cout.tie(0);int T = 1;
// cin >> T;while (T--) {Init();Solve();}return 0;
}using LL = long long;
using ULL = unsigned long long;const int Mod = 1e9 + 7;
const int Inf = 0x3f3f3f3f;
const LL InfLL = 0x3f3f3f3f3f3f3f3f;const int N = 2e5 + 10, M = 30;
int n, a[N], Log[N], Ne[N], f[N][M];
int l, r;void init_ST() {lF(i, 2, n) Log[i] = Log[i >> 1] + 1;lF(i, 1, n) f[i][0] = Ne[i] - i;for (int j = 1; (1 << j) <= n; j++)lF(i, 1, n - (1 << j) + 1)f[i][j] = std::max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
}
int ask(int L, int R) {int k = Log[R - L + 1];return std::max(f[L][k], f[R - (1 << k) + 1][k]);
}bool check(int Mid) {int x = ask(l, l + Mid - 1);return l + Mid - 1 + x <= r;
}void Init() {
}void Solve() {cin >> n;lF(i, 1, n) cin >> a[i];lF(i, 1, n) Ne[i] = std::lower_bound(a + 1, a + n + 1, 2 * a[i]) - a;init_ST();int Q; cin >> Q;while (Q--) {cin >> l >> r;int L = 0, R = r - l + 1 >> 1;while (L < R) {int Mid = L + R + 1 >> 1;if (check(Mid)) L = Mid;else R = Mid - 1;}cout << L << "\n";}
}