D. Cut and Stick
给定一个长度为 n n n的数组,里面元素为 a 1 , a 2 , a 3 , … , a n − 1 , a n , ( 1 ≤ a i ≤ n ) a_1, a_2, a_3, \dots, a_{n- 1}, a_n, (1 \leq a_i \leq n) a1,a2,a3,…,an−1,an,(1≤ai≤n),有 m m m次询问,每次给定 l , r l, r l,r,
问如果把 [ l , r ] [l, r] [l,r]划分若干段子序列,假设其中一段长度为 l e n len len,里面出现最多次的元素次数 ≤ ⌈ l e n 2 ⌉ \leq \lceil \frac{len}{2} \rceil ≤⌈2len⌉,对所有段都满足,
不考虑划分成一段的情况,那么我们就是要尽可能地把最大地给划开,最优的策略是两个最大的跟一个其他的一起划分,
假设区间长度为 r − l + 1 = l e n r - l + 1 = len r−l+1=len,最大的元素个数为 x x x,则还有 l e n − x len - x len−x个其他元素,
由于其他元素都是小于 ⌈ l e n 2 ⌉ \lceil \frac{len}{2} \rceil ⌈2len⌉的,所以,我们让两个最大元素与其他元素配对时,可以保证剩下的元素中最大个数不会超过 x x x的值,
假设我们要配对 k k k个,则当满足, x − 2 k + l e n − x − k + 1 ≥ 2 x − 4 k x - 2k + len - x - k + 1 \geq 2x - 4k x−2k+len−x−k+1≥2x−4k时只需要一个序列即可,解得 k = 2 x − l e n − 1 k = 2x - len - 1 k=2x−len−1,
所以最后答案为 m a x ( 2 x − l e n , 1 ) max(2x - len, 1) max(2x−len,1),所以我们只要统计区间数量最多的值即可,可用莫队解决。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N], ans[N], sum[N], num[N], n, m, block, maxn;
struct Res {
int l, r, id;
}query[N];
bool cmp(Res x, Res y) {
return ((x.l / block) != (y.l / block)) ? x.l < y.l : ((x.l / block) & 1) ? x.r < y.r : x.r > y.r;
}
void add(int x) {
num[a[x]]++;
sum[num[a[x]]]++;
while (sum[maxn + 1]) {
maxn++;
}
}
void del(int x) {
sum[num[a[x]]]--;
num[a[x]]--;
while (maxn && !sum[maxn]) {
maxn--;
}
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
block = sqrt(n);
for (int i = 1, l, r; i <= m; i++) {
scanf("%d %d", &l, &r);
query[i] = {l, r, i};
}
sort(query + 1, query + 1 + m, cmp);
int l = 1, r = 0;
for (int i = 1; i <= m; i++) {
while (r < query[i].r) {
add(++r);
}
while (l < query[i].l) {
del(l++);
}
while (r > query[i].r) {
del(r--);
}
while (l > query[i].l) {
add(--l);
}
ans[query[i].id] = max(1, 2 * maxn - (query[i].r - query[i].l + 1));
}
for (int i = 1; i <= m; i++) {
printf("%d\n", ans[i]);
}
return 0;
}